diff --git a/.circleci/config.yml b/.circleci/config.yml index b8084f1df..2d1e86f38 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,8 +28,9 @@ jobs: pip install "pytest==7.3.1" pip install "pytest-asyncio==0.21.1" pip install mypy - pip install "google-generativeai>=0.3.2" - pip install "google-cloud-aiplatform>=1.38.0" + pip install "google-generativeai==0.3.2" + pip install "google-cloud-aiplatform==1.43.0" + pip install pyarrow pip install "boto3>=1.28.57" pip install "aioboto3>=12.3.0" pip install langchain @@ -48,6 +49,7 @@ jobs: pip install argon2-cffi pip install "pytest-mock==3.12.0" pip install python-multipart + pip install google-cloud-aiplatform - save_cache: paths: - ./venv @@ -152,8 +154,9 @@ jobs: pip install "pytest-mock==3.12.0" pip install "pytest-asyncio==0.21.1" pip install mypy - pip install "google-generativeai>=0.3.2" - pip install "google-cloud-aiplatform>=1.38.0" + pip install "google-generativeai==0.3.2" + pip install "google-cloud-aiplatform==1.43.0" + pip install pyarrow pip install "boto3>=1.28.57" pip install "aioboto3>=12.3.0" pip install langchain diff --git a/.circleci/requirements.txt b/.circleci/requirements.txt index 4730fc28b..e9a59b7aa 100644 --- a/.circleci/requirements.txt +++ b/.circleci/requirements.txt @@ -10,5 +10,5 @@ anthropic boto3 orjson pydantic -google-cloud-aiplatform +google-cloud-aiplatform==1.43.0 redisvl==0.0.7 # semantic caching \ No newline at end of file diff --git a/.github/workflows/ghcr_deploy.yml b/.github/workflows/ghcr_deploy.yml index b863fc3fa..566199c8a 100644 --- a/.github/workflows/ghcr_deploy.yml +++ b/.github/workflows/ghcr_deploy.yml @@ -43,6 +43,14 @@ jobs: push: true file: Dockerfile.database tags: litellm/litellm-database:${{ github.event.inputs.tag || 'latest' }} + - + name: Build and push litellm-database image + uses: docker/build-push-action@v5 + with: + push: true + context: ./litellm-js/spend-logs + file: Dockerfile + tags: litellm/litellm-spend_logs:${{ github.event.inputs.tag || 'latest' }} build-and-push-image: runs-on: ubuntu-latest @@ -120,6 +128,44 @@ jobs: tags: ${{ steps.meta-database.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }}, ${{ steps.meta-database.outputs.tags }}-latest labels: ${{ steps.meta-database.outputs.labels }} platforms: local,linux/amd64,linux/arm64,linux/arm64/v8 + + build-and-push-image-spend-logs: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for database Dockerfile + id: meta-spend-logs + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-spend_logs + # Configure multi platform Docker builds + - name: Set up QEMU + uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345 + + - name: Build and push Database Docker image + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: ./litellm-js/spend-logs + file: Dockerfile + push: true + tags: ${{ steps.meta-spend-logs.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }}, ${{ steps.meta-spend-logs.outputs.tags }}-latest + labels: ${{ steps.meta-spend-logs.outputs.labels }} + platforms: local,linux/amd64,linux/arm64,linux/arm64/v8 + build-and-push-helm-chart: runs-on: ubuntu-latest steps: diff --git a/docs/my-website/docs/providers/gemini.md b/docs/my-website/docs/providers/gemini.md index 21e239d2b..628eeb999 100644 --- a/docs/my-website/docs/providers/gemini.md +++ b/docs/my-website/docs/providers/gemini.md @@ -2,6 +2,7 @@ ## Pre-requisites * `pip install -q google-generativeai` +* Get API Key - https://aistudio.google.com/ # Gemini-Pro ## Sample Usage diff --git a/docs/my-website/docs/proxy/call_hooks.md b/docs/my-website/docs/proxy/call_hooks.md index 2110af3a9..3195e2e5a 100644 --- a/docs/my-website/docs/proxy/call_hooks.md +++ b/docs/my-website/docs/proxy/call_hooks.md @@ -1,7 +1,10 @@ +import Image from '@theme/IdealImage'; + # Modify / Reject Incoming Requests - Modify data before making llm api calls on proxy - Reject data before making llm api calls / before returning the response +- Enforce 'user' param for all openai endpoint calls See a complete example with our [parallel request rate limiter](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/hooks/parallel_request_limiter.py) @@ -172,4 +175,19 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ } ], }' -``` \ No newline at end of file +``` + +## Advanced - Enforce 'user' param + +Set `enforce_user_param` to true, to require all calls to the openai endpoints to have the 'user' param. + +[**See Code**](https://github.com/BerriAI/litellm/blob/4777921a31c4c70e4d87b927cb233b6a09cd8b51/litellm/proxy/auth/auth_checks.py#L72) + +```yaml +general_settings: + enforce_user_param: True +``` + +**Result** + + \ No newline at end of file diff --git a/docs/my-website/docs/proxy/configs.md b/docs/my-website/docs/proxy/configs.md index b2a7e42ba..e83125f2a 100644 --- a/docs/my-website/docs/proxy/configs.md +++ b/docs/my-website/docs/proxy/configs.md @@ -602,6 +602,7 @@ general_settings: "disable_spend_logs": "boolean", # turn off writing each transaction to the db "disable_reset_budget": "boolean", # turn off reset budget scheduled task "enable_jwt_auth": "boolean", # allow proxy admin to auth in via jwt tokens with 'litellm_proxy_admin' in claims + "enforce_user_param": "boolean", # requires all openai endpoint requests to have a 'user' param "allowed_routes": "list", # list of allowed proxy API routes - a user can access. (currently JWT-Auth only) "key_management_system": "google_kms", # either google_kms or azure_kms "master_key": "string", diff --git a/docs/my-website/docs/proxy/prod.md b/docs/my-website/docs/proxy/prod.md index a4dbf6b5f..f287aea64 100644 --- a/docs/my-website/docs/proxy/prod.md +++ b/docs/my-website/docs/proxy/prod.md @@ -40,7 +40,13 @@ Use this Docker `CMD`. This will start the proxy with 1 Uvicorn Async Worker CMD ["--port", "4000", "--config", "./proxy_server_config.yaml"] ``` -## 3. Switch off spend logging and resetting budgets +## 3. Move spend logs to separate server + +Writing each spend log to the db can slow down your proxy. In testing we saw a 70% improvement in median response time, by moving writing spend logs to a separate server. + + + +## 4. Switch off resetting budgets Add this to your config.yaml. (Only spend per Key, User and Team will be tracked - spend per API Call will not be written to the LiteLLM Database) ```yaml @@ -49,7 +55,7 @@ general_settings: disable_reset_budget: true ``` -## 4. Switch of `litellm.telemetry` +## 5. Switch of `litellm.telemetry` Switch of all telemetry tracking done by litellm diff --git a/docs/my-website/docs/proxy/ui.md b/docs/my-website/docs/proxy/ui.md index cca9d4434..2ffa43952 100644 --- a/docs/my-website/docs/proxy/ui.md +++ b/docs/my-website/docs/proxy/ui.md @@ -47,8 +47,9 @@ Your Proxy Swagger is available on the root of the Proxy: e.g.: `http://localhos Set the following in your .env on the Proxy ```shell -UI_USERNAME=ishaan-litellm -UI_PASSWORD=langchain +LITELLM_MASTER_KEY="sk-1234" # this is your master key for using the proxy server +UI_USERNAME=ishaan-litellm # username to sign in on UI +UI_PASSWORD=langchain # password to sign in on UI ``` On accessing the LiteLLM UI, you will be prompted to enter your username, password diff --git a/docs/my-website/img/end_user_enforcement.png b/docs/my-website/img/end_user_enforcement.png new file mode 100644 index 000000000..2de7b7e18 Binary files /dev/null and b/docs/my-website/img/end_user_enforcement.png differ diff --git a/litellm-js/spend-logs/Dockerfile b/litellm-js/spend-logs/Dockerfile new file mode 100644 index 000000000..7bd4fefba --- /dev/null +++ b/litellm-js/spend-logs/Dockerfile @@ -0,0 +1,26 @@ +# Use the specific Node.js v20.11.0 image +FROM node:20.11.0 + +# Set the working directory inside the container +WORKDIR /usr/src/app + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Install Prisma globally +RUN npm install -g prisma + +# Copy the rest of the application code +COPY . . + +# Generate Prisma client +RUN npx prisma generate + +# Expose the port that the Node.js server will run on +EXPOSE 3000 + +# Command to run the Node.js app with npm run dev +CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/litellm-js/spend-logs/README.md b/litellm-js/spend-logs/README.md new file mode 100644 index 000000000..e12b31db7 --- /dev/null +++ b/litellm-js/spend-logs/README.md @@ -0,0 +1,8 @@ +``` +npm install +npm run dev +``` + +``` +open http://localhost:3000 +``` diff --git a/litellm-js/spend-logs/package-lock.json b/litellm-js/spend-logs/package-lock.json new file mode 100644 index 000000000..b74f5e9d7 --- /dev/null +++ b/litellm-js/spend-logs/package-lock.json @@ -0,0 +1,508 @@ +{ + "name": "spend-logs", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@hono/node-server": "^1.9.0", + "hono": "^4.1.5" + }, + "devDependencies": { + "@types/node": "^20.11.17", + "tsx": "^4.7.1" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@hono/node-server": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.9.0.tgz", + "integrity": "sha512-oJjk7WXBlENeHhWiMqSyxPIZ3Kmf5ZYxqdlcSIXyN8Rn50bNJsPl99G4POBS03Jxh56FdfRJ0SEnC8mAVIiavQ==", + "engines": { + "node": ">=18.14.1" + } + }, + "node_modules/@types/node": { + "version": "20.11.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", + "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", + "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/hono": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.1.5.tgz", + "integrity": "sha512-3ChJiIoeCxvkt6vnkxJagplrt1YZg3NyNob7ssVeK2PUqEINp4q1F94HzFnvY9QE8asVmbW5kkTDlyWylfg2vg==", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz", + "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==", + "dev": true, + "dependencies": { + "esbuild": "~0.19.10", + "get-tsconfig": "^4.7.2" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + } +} diff --git a/litellm-js/spend-logs/package.json b/litellm-js/spend-logs/package.json new file mode 100644 index 000000000..0fc9b11af --- /dev/null +++ b/litellm-js/spend-logs/package.json @@ -0,0 +1,13 @@ +{ + "scripts": { + "dev": "tsx watch src/index.ts" + }, + "dependencies": { + "@hono/node-server": "^1.9.0", + "hono": "^4.1.5" + }, + "devDependencies": { + "@types/node": "^20.11.17", + "tsx": "^4.7.1" + } +} diff --git a/litellm-js/spend-logs/schema.prisma b/litellm-js/spend-logs/schema.prisma new file mode 100644 index 000000000..b0403f277 --- /dev/null +++ b/litellm-js/spend-logs/schema.prisma @@ -0,0 +1,29 @@ +generator client { + provider = "prisma-client-js" +} + +datasource client { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model LiteLLM_SpendLogs { + request_id String @id + call_type String + api_key String @default("") + spend Float @default(0.0) + total_tokens Int @default(0) + prompt_tokens Int @default(0) + completion_tokens Int @default(0) + startTime DateTime + endTime DateTime + model String @default("") + api_base String @default("") + user String @default("") + metadata Json @default("{}") + cache_hit String @default("") + cache_key String @default("") + request_tags Json @default("[]") + team_id String? + end_user String? +} \ No newline at end of file diff --git a/litellm-js/spend-logs/src/_types.ts b/litellm-js/spend-logs/src/_types.ts new file mode 100644 index 000000000..6a9b49917 --- /dev/null +++ b/litellm-js/spend-logs/src/_types.ts @@ -0,0 +1,32 @@ +export type LiteLLM_IncrementSpend = { + key_transactions: Array, // [{"key": spend},..] + user_transactions: Array, + team_transactions: Array, + spend_logs_transactions: Array +} + +export type LiteLLM_IncrementObject = { + key: string, + spend: number +} + +export type LiteLLM_SpendLogs = { + request_id: string; // @id means it's a unique identifier + call_type: string; + api_key: string; // @default("") means it defaults to an empty string if not provided + spend: number; // Float in Prisma corresponds to number in TypeScript + total_tokens: number; // Int in Prisma corresponds to number in TypeScript + prompt_tokens: number; + completion_tokens: number; + startTime: Date; // DateTime in Prisma corresponds to Date in TypeScript + endTime: Date; + model: string; // @default("") means it defaults to an empty string if not provided + api_base: string; + user: string; + metadata: any; // Json type in Prisma is represented by any in TypeScript; could also use a more specific type if the structure of JSON is known + cache_hit: string; + cache_key: string; + request_tags: any; // Similarly, this could be an array or a more specific type depending on the expected structure + team_id?: string | null; // ? indicates it's optional and can be undefined, but could also be null if not provided + end_user?: string | null; +}; \ No newline at end of file diff --git a/litellm-js/spend-logs/src/index.ts b/litellm-js/spend-logs/src/index.ts new file mode 100644 index 000000000..3581d95c8 --- /dev/null +++ b/litellm-js/spend-logs/src/index.ts @@ -0,0 +1,84 @@ +import { serve } from '@hono/node-server' +import { Hono } from 'hono' +import { PrismaClient } from '@prisma/client' +import {LiteLLM_SpendLogs, LiteLLM_IncrementSpend, LiteLLM_IncrementObject} from './_types' + +const app = new Hono() +const prisma = new PrismaClient() +// In-memory storage for logs +let spend_logs: LiteLLM_SpendLogs[] = []; +const key_logs: LiteLLM_IncrementObject[] = []; +const user_logs: LiteLLM_IncrementObject[] = []; +const transaction_logs: LiteLLM_IncrementObject[] = []; + + +app.get('/', (c) => { + return c.text('Hello Hono!') +}) + +const MIN_LOGS = 1; // Minimum number of logs needed to initiate a flush +const FLUSH_INTERVAL = 5000; // Time in ms to wait before trying to flush again +const BATCH_SIZE = 100; // Preferred size of each batch to write to the database +const MAX_LOGS_PER_INTERVAL = 1000; // Maximum number of logs to flush in a single interval + +const flushLogsToDb = async () => { + if (spend_logs.length >= MIN_LOGS) { + // Limit the logs to process in this interval to MAX_LOGS_PER_INTERVAL or less + const logsToProcess = spend_logs.slice(0, MAX_LOGS_PER_INTERVAL); + + for (let i = 0; i < logsToProcess.length; i += BATCH_SIZE) { + // Create subarray for current batch, ensuring it doesn't exceed the BATCH_SIZE + const batch = logsToProcess.slice(i, i + BATCH_SIZE); + + // Convert datetime strings to Date objects + const batchWithDates = batch.map(entry => ({ + ...entry, + startTime: new Date(entry.startTime), + endTime: new Date(entry.endTime), + // Repeat for any other DateTime fields you may have + })); + + await prisma.liteLLM_SpendLogs.createMany({ + data: batchWithDates, + }); + + console.log(`Flushed ${batch.length} logs to the DB.`); + } + + // Remove the processed logs from spend_logs + spend_logs = spend_logs.slice(logsToProcess.length); + + console.log(`${logsToProcess.length} logs processed. Remaining in queue: ${spend_logs.length}`); + } else { + // This will ensure it doesn't falsely claim "No logs to flush." when it's merely below the MIN_LOGS threshold. + if(spend_logs.length > 0) { + console.log(`Accumulating logs. Currently at ${spend_logs.length}, waiting for at least ${MIN_LOGS}.`); + } else { + console.log("No logs to flush."); + } + } +}; + +// Setup interval for attempting to flush the logs +setInterval(flushLogsToDb, FLUSH_INTERVAL); + +// Route to receive log messages +app.post('/spend/update', async (c) => { + const incomingLogs = await c.req.json(); + + spend_logs.push(...incomingLogs); + + console.log(`Received and stored ${incomingLogs.length} logs. Total logs in memory: ${spend_logs.length}`); + + return c.json({ message: `Successfully stored ${incomingLogs.length} logs` }); +}); + + + +const port = 3000 +console.log(`Server is running on port ${port}`) + +serve({ + fetch: app.fetch, + port +}) diff --git a/litellm-js/spend-logs/tsconfig.json b/litellm-js/spend-logs/tsconfig.json new file mode 100644 index 000000000..028c03b6a --- /dev/null +++ b/litellm-js/spend-logs/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "types": [ + "node" + ], + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx", + } +} \ No newline at end of file diff --git a/litellm/llms/custom_httpx/httpx_handler.py b/litellm/llms/custom_httpx/httpx_handler.py new file mode 100644 index 000000000..3f3bd09ba --- /dev/null +++ b/litellm/llms/custom_httpx/httpx_handler.py @@ -0,0 +1,38 @@ +from typing import Optional +import httpx + + +class HTTPHandler: + def __init__(self, concurrent_limit=1000): + # Create a client with a connection pool + self.client = httpx.AsyncClient( + limits=httpx.Limits( + max_connections=concurrent_limit, + max_keepalive_connections=concurrent_limit, + ) + ) + + async def close(self): + # Close the client when you're done with it + await self.client.aclose() + + async def get( + self, url: str, params: Optional[dict] = None, headers: Optional[dict] = None + ): + response = await self.client.get(url, params=params, headers=headers) + return response + + async def post( + self, + url: str, + data: Optional[dict] = None, + params: Optional[dict] = None, + headers: Optional[dict] = None, + ): + try: + response = await self.client.post( + url, data=data, params=params, headers=headers + ) + return response + except Exception as e: + raise e diff --git a/litellm/main.py b/litellm/main.py index 0a44e0097..b0d9c5534 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -13,6 +13,7 @@ from functools import partial import dotenv, traceback, random, asyncio, time, contextvars from copy import deepcopy import httpx + import litellm from ._logging import verbose_logger from litellm import ( # type: ignore diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html index 423a55d1f..81b64a1b0 100644 --- a/litellm/proxy/_experimental/out/404.html +++ b/litellm/proxy/_experimental/out/404.html @@ -1 +1 @@ -404: This page could not be found.🚅 LiteLLM

404

This page could not be found.

\ No newline at end of file +404: This page could not be found.🚅 LiteLLM

404

This page could not be found.

\ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/aIO8mtlEIEUTmgL8cGjve/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/aIO8mtlEIEUTmgL8cGjve/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/aIO8mtlEIEUTmgL8cGjve/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/aIO8mtlEIEUTmgL8cGjve/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/app/layout-a188d05d2ecab3a2.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/layout-24ae10436e315256.js similarity index 60% rename from litellm/proxy/_experimental/out/_next/static/chunks/app/layout-a188d05d2ecab3a2.js rename to litellm/proxy/_experimental/out/_next/static/chunks/app/layout-24ae10436e315256.js index fe5260feb..e261adc05 100644 --- a/litellm/proxy/_experimental/out/_next/static/chunks/app/layout-a188d05d2ecab3a2.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/app/layout-24ae10436e315256.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[185],{11837:function(n,e,t){Promise.resolve().then(t.t.bind(t,99646,23)),Promise.resolve().then(t.t.bind(t,63385,23))},63385:function(){},99646:function(n){n.exports={style:{fontFamily:"'__Inter_c23dc8', '__Inter_Fallback_c23dc8'",fontStyle:"normal"},className:"__className_c23dc8"}}},function(n){n.O(0,[971,69,744],function(){return n(n.s=11837)}),_N_E=n.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[185],{87421:function(n,e,t){Promise.resolve().then(t.t.bind(t,99646,23)),Promise.resolve().then(t.t.bind(t,63385,23))},63385:function(){},99646:function(n){n.exports={style:{fontFamily:"'__Inter_c23dc8', '__Inter_Fallback_c23dc8'",fontStyle:"normal"},className:"__className_c23dc8"}}},function(n){n.O(0,[971,69,744],function(){return n(n.s=87421)}),_N_E=n.O()}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-144687b251040a22.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-144687b251040a22.js deleted file mode 100644 index 761b3cd14..000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-144687b251040a22.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{79615:function(e,t,s){Promise.resolve().then(s.bind(s,19914))},19914:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return eM}});var l=s(3827),r=s(64090),n=s(47907),a=s(8792),o=s(2179),i=e=>{let{userID:t,userRole:s,userEmail:r,showSSOBanner:n}=e;return console.log("User ID:",t),console.log("userEmail:",r),(0,l.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,l.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,l.jsx)("div",{className:"flex flex-col items-center",children:(0,l.jsx)(a.default,{href:"/",children:(0,l.jsx)("button",{className:"text-gray-800 text-2xl py-1 rounded text-center",children:(0,l.jsx)("img",{src:"/get_image",width:200,height:200,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,l.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[n?(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#setup-ssoauth-for-ui",target:"_blank",className:"mr-2",children:(0,l.jsx)(o.Z,{variant:"primary",size:"lg",children:"Enable SSO"})}):null,(0,l.jsxs)(o.Z,{variant:"secondary",size:"lg",children:[r,(0,l.jsxs)("p",{children:["Role: ",s]}),(0,l.jsxs)("p",{children:["ID: ",t]})]})]})]})},c=s(80588);let d=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await l.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/user/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await l.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t);let s=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},u=async function(e,t,s){let l=arguments.length>3&&void 0!==arguments[3]&&arguments[3];try{let r="/user/info";"App Owner"==s&&t&&(r="".concat(r,"?user_id=").concat(t)),console.log("in userInfoCall viewAll=",l),l&&(r="".concat(r,"?view_all=true"));let n=await fetch(r,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let a=await n.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},x=async e=>{try{let t=await fetch("/global/spend",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,t,s)=>{try{let t=await fetch("/v2/model/info",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t,s)=>{try{let t=await fetch("/model/metrics",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,t,s)=>{try{let t=await fetch("/models",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let s="/global/spend/logs";console.log("in keySpendLogsCall:",s);let l=await fetch("".concat(s,"?api_key=").concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let r=await l.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},w=async e=>{try{let t="/global/spend/teams";console.log("in teamSpendLogsCall:",t);let s=await fetch("".concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},Z=async(e,t,s,l,r,n)=>{try{console.log("user role in spend logs call: ".concat(s));let t="/spend/logs";t="App Owner"==s?"".concat(t,"?user_id=").concat(l,"&start_date=").concat(r,"&end_date=").concat(n):"".concat(t,"?start_date=").concat(r,"&end_date=").concat(n);let a=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw c.ZP.error(e),Error("Network response was not ok")}let o=await a.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},f=async e=>{try{let t=await fetch("/global/spend/logs",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},k=async e=>{try{let t=await fetch("/global/spend/keys?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,t)=>{try{t&&JSON.stringify({api_key:t});let s={method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}};t&&(s.body=JSON.stringify({api_key:t}));let l=await fetch("/global/spend/end_users",s);if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let r=await l.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},b=async e=>{try{let t=await fetch("/global/spend/models?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},v=async(e,t)=>{try{let s=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},S=async(e,t,s,l)=>{try{let r=await fetch("/user/request_model",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({models:[t],user_id:s,justification:l})});if(!r.ok){let e=await r.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let n=await r.json();return console.log(n),n}catch(e){throw console.error("Failed to create key:",e),e}},N=async e=>{try{let t="/user/get_requests";console.log("in userGetRequesedtModelsCall:",t);let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to get requested models:",e),e}},A=async(e,t)=>{try{let s="/user/get_users?role=".concat(t);console.log("in userGetAllUsersCall:",s);let l=await fetch(s,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let r=await l.json();return console.log(r),r}catch(e){throw console.error("Failed to get requested models:",e),e}},C=async(e,t)=>{try{console.log("Form Values in teamCreateCall:",t);let s=await fetch("/team/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let l=await s.json();return console.log("API Response:",l),l}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,t,s)=>{try{console.log("Form Values in teamMemberAddCall:",s);let l=await fetch("/team/member_add",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await l.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},I=async(e,t)=>{try{console.log("Form Values in userUpdateUserCall:",t);let s=await fetch("/user/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_role:"proxy_admin_viewer",...t})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let l=await s.json();return console.log("API Response:",l),l}catch(e){throw console.error("Failed to create key:",e),e}},E=async(e,t)=>{try{let s=await fetch("/global/predict/spend/logs",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({data:t})});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},P=async e=>{try{console.log("Checking Slack Budget Alerts service health");let t=await fetch("/health/services?service=slack_budget_alerts",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error("Failed Slack Alert test: "+e),Error(e)}let s=await t.json();return c.ZP.success("Test Slack Alert worked - check your Slack!"),console.log("Service Health Response:",s),s}catch(e){throw console.error("Failed to perform health check:",e),e}};var F=s(10384),O=s(46453),M=s(13810),R=s(71801),D=s(42440),U=s(17189),L=s(12143),B=s(77171),z=s(42539),K=s(88707),V=s(1861);let{Option:W}=U.default;var q=e=>{let{userID:t,teamID:s,userRole:n,accessToken:a,data:i,userModels:h,setData:m}=e,[u]=L.Z.useForm(),[x,p]=(0,r.useState)(!1),[j,y]=(0,r.useState)(null),[g,w]=(0,r.useState)(null),Z=()=>{p(!1),u.resetFields()},f=()=>{p(!1),y(null),u.resetFields()},k=async e=>{try{c.ZP.info("Making API Call"),p(!0);let s=await d(a,t,e);console.log("key create Response:",s),m(e=>e?[...e,s]:[s]),y(s.key),w(s.soft_budget),c.ZP.success("API Key Created"),u.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}},_=async()=>{try{console.log("Sending Slack alert...");let e=await P(a);console.log("slackBudgetAlertsHealthCheck Response:",e),console.log("Testing Slack alert successful")}catch(e){console.error("Error sending Slack alert:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>p(!0),children:"+ Create New Key"}),(0,l.jsx)(B.Z,{title:"Create Key",visible:x,width:800,footer:null,onOk:Z,onCancel:f,children:(0,l.jsxs)(L.Z,{form:u,onFinish:k,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Team ID",name:"team_id",children:(0,l.jsx)(z.Z,{placeholder:"ai_team",defaultValue:s||""})}),(0,l.jsx)(L.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(U.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:h.map(e=>(0,l.jsx)(W,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Soft Budget (USD) Monthly",name:"soft_budget",initialValue:50,children:(0,l.jsx)(K.Z,{step:.01,precision:2,defaultValue:50,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(K.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Reset Budget",name:"budget_duration",children:(0,l.jsxs)(U.default,{defaultValue:null,placeholder:"n/a",children:[(0,l.jsx)(U.default.Option,{value:"24h",children:"daily"}),(0,l.jsx)(U.default.Option,{value:"30d",children:"monthly"})]})}),(0,l.jsx)(L.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(z.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,l.jsx)(z.Z,{placeholder:"ai_team"})}),(0,l.jsx)(L.Z.Item,{label:"Description",name:"description",children:(0,l.jsx)(z.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(V.ZP,{htmlType:"submit",children:"Create Key"})})]})}),j&&(0,l.jsx)(B.Z,{visible:x,onOk:Z,onCancel:f,footer:null,children:(0,l.jsx)(O.Z,{numItems:1,className:"gap-2 w-full",children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Save your Key"}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,l.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,l.jsx)(F.Z,{numColSpan:1,children:null!=j?(0,l.jsxs)("div",{children:[(0,l.jsxs)(R.Z,{children:["API Key: ",j]}),(0,l.jsx)(D.Z,{className:"mt-6",children:"Budgets"}),(0,l.jsxs)(R.Z,{children:["Soft Limit Budget: $",g]}),(0,l.jsx)(o.Z,{className:"mt-3",onClick:_,children:"Test Slack Alert"}),(0,l.jsxs)(R.Z,{className:"mt-2",children:["(LiteLLM Docs -",(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/alerting",target:"_blank",className:"text-blue-500",children:"Set Up Slack Alerting)"})]})]}):(0,l.jsx)(R.Z,{children:"Key being created, this might take 30s"})})]})})})]})},G=s(33393),J=s(61244),$=s(10827),H=s(3851),Y=s(2044),X=s(64167),Q=s(74480),ee=s(7178),et=s(9853),es=s(56863),el=e=>{let{token:t,accessToken:s,keySpend:n,keyBudget:a,keyName:i}=e,[c,d]=(0,r.useState)(!1),[h,m]=(0,r.useState)(null),[u,x]=(0,r.useState)(""),[p,j]=(0,r.useState)(null),y=async()=>{try{if(null==s||null==t)return;console.log("accessToken: ".concat(s,"; token: ").concat(t));let e=await g(s,t);console.log("Response:",e),m(e);let l=await E(s,e);console.log("Response2:",l);let r=[...e,...l.response];m(r),x(l.predicted_spend),console.log("Combined Data:",r)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{size:"xs",onClick:()=>{console.log("Show Modal triggered"),d(!0),y()},variant:"secondary",children:"Spend Report"}),(0,l.jsxs)(B.Z,{visible:c,width:1400,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,l.jsxs)(D.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,l.jsxs)(es.Z,{children:["Monthly Spend $",n]}),(0,l.jsx)(D.Z,{children:u}),(0,l.jsx)(M.Z,{className:"mt-6 mb-6",children:h&&(0,l.jsx)(et.Z,{className:"mt-6",data:h,colors:["blue","amber"],index:"date",categories:["spend","predicted_spend"],yAxisWidth:80})})]})]}):null},er=e=>{let{userID:t,accessToken:s,data:n,setData:a}=e,[i,c]=(0,r.useState)(!1),[d,h]=(0,r.useState)(!1),[u,x]=(0,r.useState)(null),p=async e=>{null!=n&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=n){try{await m(s,u);let e=n.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=n)return console.log("RERENDER TRIGGERED"),(0,l.jsxs)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,l.jsxs)($.Z,{className:"mt-5",children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Key Alias"}),(0,l.jsx)(Q.Z,{children:"Secret Key"}),(0,l.jsx)(Q.Z,{children:"Spend (USD)"}),(0,l.jsx)(Q.Z,{children:"Key Budget (USD)"}),(0,l.jsx)(Q.Z,{children:"Spend Report"}),(0,l.jsx)(Q.Z,{children:"Team ID"}),(0,l.jsx)(Q.Z,{children:"Metadata"}),(0,l.jsx)(Q.Z,{children:"Models"}),(0,l.jsx)(Q.Z,{children:"TPM / RPM Limits"}),(0,l.jsx)(Q.Z,{children:"Expires"})]})}),(0,l.jsx)(H.Z,{children:n.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,l.jsx)(R.Z,{children:e.key_alias}):(0,l.jsx)(R.Z,{children:"Not Set"})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:e.key_name})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(t){return e.spend}})()})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.max_budget?(0,l.jsx)(R.Z,{children:e.max_budget}):(0,l.jsx)(R.Z,{children:"Unlimited Budget"})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px"},children:(0,l.jsx)(el,{token:e.token,accessToken:s,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:e.team_id})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:JSON.stringify(e.metadata).slice(0,400)})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:JSON.stringify(e.models)})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",overflowWrap:"break-word"},children:(0,l.jsxs)(R.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",wordWrap:"break-word"},children:null!=e.expires?(0,l.jsx)(R.Z,{children:e.expires}):(0,l.jsx)(R.Z,{children:"Never"})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",wordWrap:"break-word"},children:(0,l.jsx)(J.Z,{onClick:()=>p(e.token),icon:G.Z,size:"sm"})})]},e.token))})]}),d&&(0,l.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,l.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,l.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,l.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,l.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,l.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,l.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,l.jsx)("div",{className:"sm:flex sm:items-start",children:(0,l.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,l.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,l.jsx)("div",{className:"mt-2",children:(0,l.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,l.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,l.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,l.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},en=e=>{let{userID:t,userSpendData:s,userRole:n,accessToken:a}=e,[o,i]=(0,r.useState)(null==s?void 0:s.spend),[c,d]=(0,r.useState)((null==s?void 0:s.max_budget)||null);(0,r.useEffect)(()=>{(async()=>{if("Admin"===n)try{let e=await x(a);i(e.spend),d(e.max_budget||null)}catch(e){console.error("Error fetching global spend data:",e)}})()},[n,a]);let h=void 0!==o?o.toFixed(4):null;return(0,l.jsx)(l.Fragment,{children:(0,l.jsxs)(M.Z,{className:"mx-auto mb-4",children:[(0,l.jsxs)(es.Z,{children:["$",h]}),(0,l.jsxs)(D.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},ea=s(36083),eo=s(68967),ei=s(27166),ec=e=>{let{teams:t,setSelectedTeam:s}=e,{Title:n,Paragraph:a}=ea.default,[o,i]=(0,r.useState)("");return(0,l.jsxs)("div",{className:"mt-10",children:[(0,l.jsx)(n,{level:4,children:"Default Team"}),(0,l.jsx)(a,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),t&&t.length>0?(0,l.jsx)(eo.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(ei.Z,{value:String(t),onClick:()=>s(e),children:e.team_alias},t))}):(0,l.jsxs)(a,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]})},ed=s(37963);console.log("isLocal:",!1);var eh=e=>{let{userID:t,userRole:s,teams:a,keys:o,setUserRole:i,userEmail:c,setUserEmail:d,setTeams:h,setKeys:m}=e,[p,j]=(0,r.useState)(null),g=(0,n.useSearchParams)();g.get("viewSpend"),(0,n.useRouter)();let w=g.get("token"),[Z,f]=(0,r.useState)(null),[k,_]=(0,r.useState)([]),[b,v]=(0,r.useState)(a?a[0]:null);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,r.useEffect)(()=>{if(w){let e=(0,ed.o)(w);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),f(e.key),e.user_role){let t=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e)),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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),i(t)}else console.log("User role not defined");e.user_email?d(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&Z&&s&&!o&&!p){let e=sessionStorage.getItem("userModels"+t);e?_(JSON.parse(e)):(async()=>{try{let e=await u(Z,t,s);if(console.log("received teams in user dashboard: ".concat(Object.keys(e),"; team values: ").concat(Object.entries(e.teams))),"Admin"==s){let e=await x(Z);j(e),console.log("globalSpend:",e)}else j(e.user_info);m(e.keys),h(e.teams),v(e.teams?e.teams[0]:null),sessionStorage.setItem("userData"+t,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let l=(await y(Z,t,s)).data.map(e=>e.id);console.log("available_model_names:",l),_(l),console.log("userModels:",k),sessionStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,w,Z,o,s]),null==t||null==w){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}if(null==Z)return null;if(null==s&&i("App Owner"),s&&"Admin Viewer"==s){let{Title:e,Paragraph:t}=ea.default;return(0,l.jsxs)("div",{children:[(0,l.jsx)(e,{level:1,children:"Access Denied"}),(0,l.jsx)(t,{children:"Ask your proxy admin for access to create keys"})]})}return(0,l.jsx)("div",{children:(0,l.jsx)(O.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(en,{userID:t,userSpendData:p,userRole:s,accessToken:Z}),(0,l.jsx)(er,{userID:t,accessToken:Z,data:o,setData:m}),(0,l.jsx)(q,{userID:t,teamID:b?b.team_id:null,userRole:s,userModels:k,accessToken:Z,data:o,setData:m}),(0,l.jsx)(ec,{teams:a,setSelectedTeam:v})]})})})},em=s(5);let{Option:eu}=U.default;var ex=e=>{let{userModels:t,accessToken:s,userID:n}=e,[a]=L.Z.useForm(),[i,d]=(0,r.useState)(!1),h=async e=>{try{c.ZP.info("Requesting access");let{selectedModel:t,accessReason:l}=e;await S(s,t,n,l),d(!0)}catch(e){console.error("Error requesting access:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{size:"xs",onClick:()=>d(!0),children:"Request Access"}),(0,l.jsx)(B.Z,{title:"Request Access",visible:i,width:800,footer:null,onOk:()=>{d(!1),a.resetFields()},onCancel:()=>{d(!1),a.resetFields()},children:(0,l.jsxs)(L.Z,{form:a,onFinish:h,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(L.Z.Item,{label:"Select Model",name:"selectedModel",children:(0,l.jsx)(U.default,{placeholder:"Select model",style:{width:"100%"},children:t.map(e=>(0,l.jsx)(eu,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Reason for Access",name:"accessReason",children:(0,l.jsx)(z.Z.TextArea,{rows:4,placeholder:"Enter reason for access"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(o.Z,{children:"Request Access"})})]})})]})},ep=e=>{let{accessToken:t,token:s,userRole:n,userID:a}=e,[o,i]=(0,r.useState)({data:[]}),[c,d]=(0,r.useState)([]),[h,m]=(0,r.useState)([]);if((0,r.useEffect)(()=>{if(!t||!s||!n||!a)return;let e=async()=>{try{let e=await p(t,a,n);console.log("Model data response:",e.data),i(e);let s=await j(t,a,n);if(console.log("Model metrics response:",s),d(s),"Admin"===n&&t){let e=await N(t);console.log("Pending Requests:",h),m(e.requests||[])}}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&n&&a&&e()},[t,s,n,a]),!o||!t||!s||!n||!a)return(0,l.jsx)("div",{children:"Loading..."});let u=[];for(let e=0;e(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:(0,l.jsx)(D.Z,{children:e.model_name})}),(0,l.jsx)(Y.Z,{children:e.provider}),"Admin"===n&&(0,l.jsx)(Y.Z,{children:e.api_base}),(0,l.jsx)(Y.Z,{children:e.user_access?(0,l.jsx)(em.Z,{color:"green",children:"Yes"}):(0,l.jsx)(ex,{userModels:u,accessToken:t,userID:a})}),(0,l.jsx)(Y.Z,{children:e.input_cost}),(0,l.jsx)(Y.Z,{children:e.output_cost}),(0,l.jsx)(Y.Z,{children:e.max_tokens})]},e.model_name))})]})}),(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Model Statistics (Number Requests)"}),(0,l.jsx)(et.Z,{data:c,index:"model",categories:["num_requests"],colors:["blue"],yAxisWidth:400,layout:"vertical",tickGap:5})]}),(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Model Statistics (Latency)"}),(0,l.jsx)(et.Z,{data:c,index:"model",categories:["avg_latency_seconds"],colors:["red"],yAxisWidth:400,layout:"vertical",tickGap:5})]})]})})},ej=s(92836),ey=s(26734),eg=s(41608),ew=s(32126),eZ=s(23682);let{Option:ef}=U.default;var ek=e=>{let{userID:t,accessToken:s}=e,[n]=L.Z.useForm(),[a,i]=(0,r.useState)(!1),[d,m]=(0,r.useState)(null),[u,x]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{let e=await y(s,t,"any"),l=[];for(let t=0;t{i(!1),n.resetFields()},j=()=>{i(!1),m(null),n.resetFields()},g=async e=>{try{c.ZP.info("Making API Call"),i(!0),console.log("formValues in create user:",e);let l=await h(s,t,e);console.log("user create Response:",l),m(l.key),c.ZP.success("API user Created"),n.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the user:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>i(!0),children:"+ Create New User"}),(0,l.jsx)(B.Z,{title:"Create User",visible:a,width:800,footer:null,onOk:p,onCancel:j,children:(0,l.jsxs)(L.Z,{form:n,onFinish:g,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(L.Z.Item,{label:"User ID",name:"user_id",children:(0,l.jsx)(z.Z,{placeholder:"Enter User ID"})}),(0,l.jsx)(L.Z.Item,{label:"Team ID",name:"team_id",children:(0,l.jsx)(z.Z,{placeholder:"ai_team"})}),(0,l.jsx)(L.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(U.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:u.map(e=>(0,l.jsx)(ef,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(K.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(z.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(V.ZP,{htmlType:"submit",children:"Create User"})})]})}),d&&(0,l.jsxs)(B.Z,{title:"Save Your User",visible:a,onOk:p,onCancel:j,footer:null,children:[(0,l.jsxs)("p",{children:["Please save this secret user somewhere safe and accessible. For security reasons, ",(0,l.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret user, you will need to generate a new one."]}),(0,l.jsx)("p",{children:null!=d?"API user: ".concat(d):"User being created, this might take 30s"})]})]})},e_=e=>{let{accessToken:t,token:s,keys:n,userRole:a,userID:o,setKeys:i}=e,[c,d]=(0,r.useState)(null),[h,m]=(0,r.useState)(null),[x,p]=(0,r.useState)(1);if((0,r.useEffect)(()=>{if(!t||!s||!a||!o)return;let e=async()=>{try{let e=await u(t,null,a,!0);console.log("user data response:",e),d(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&a&&o&&!c&&e();let l=async()=>{try{let e=await _(t,null);console.log("user data response:",e),m(e)}catch(e){console.error("There was an error fetching the model data",e)}};a&&("Admin"==a||"Admin Viewer"==a)&&!h&&l()},[t,s,a,o]),!c||!t||!s||!a||!o)return(0,l.jsx)("div",{children:"Loading..."});let j=async e=>{try{let s=await _(t,e);console.log("user data response:",s),m(s)}catch(e){console.error("There was an error fetching the model data",e)}};return(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(O.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(ek,{userID:o,accessToken:t}),(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{variant:"line",defaultValue:"1",children:[(0,l.jsx)(ej.Z,{value:"1",children:"Key Owners"}),(0,l.jsx)(ej.Z,{value:"2",children:"End-Users"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsx)(ew.Z,{children:(0,l.jsxs)($.Z,{className:"mt-5",children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"User ID"}),(0,l.jsx)(Q.Z,{children:"User Role"}),(0,l.jsx)(Q.Z,{children:"User Models"}),(0,l.jsx)(Q.Z,{children:"User Spend ($ USD)"}),(0,l.jsx)(Q.Z,{children:"User Max Budget ($ USD)"})]})}),(0,l.jsx)(H.Z,{children:c.map(e=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.user_id}),(0,l.jsx)(Y.Z,{children:e.user_role?e.user_role:"app_owner"}),(0,l.jsx)(Y.Z,{children:e.models&&e.models.length>0?e.models:"All Models"}),(0,l.jsx)(Y.Z,{children:e.spend?e.spend:0}),(0,l.jsx)(Y.Z,{children:e.max_budget?e.max_budget:"Unlimited"})]},e.user_id))})]})}),(0,l.jsxs)(ew.Z,{children:[(0,l.jsxs)("div",{className:"flex items-center",children:[(0,l.jsx)("div",{className:"flex-1"}),(0,l.jsxs)("div",{className:"flex-1 flex justify-between items-center",children:[(0,l.jsx)(R.Z,{className:"w-1/4 mr-2 text-right",children:"Key"}),(0,l.jsx)(eo.Z,{defaultValue:"1",className:"w-3/4",children:null==n?void 0:n.map((e,t)=>{if(e&&null!==e.key_name&&e.key_name.length>0)return(0,l.jsx)(ei.Z,{value:String(t),onClick:()=>j(e.token),children:e.key_name},t)})})]})]}),(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"End User"}),(0,l.jsx)(Q.Z,{children:"Spend"}),(0,l.jsx)(Q.Z,{children:"Total Events"})]})}),(0,l.jsx)(H.Z,{children:null==h?void 0:h.map((e,t)=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.end_user}),(0,l.jsx)(Y.Z,{children:e.total_spend}),(0,l.jsx)(Y.Z,{children:e.total_events})]},t))})]})]})]})]})}),function(){if(!c)return null;let e=Math.ceil(c.length/25),t=Math.min(25*x,c.length);return(0,l.jsxs)("div",{className:"flex justify-between items-center",children:[(0,l.jsxs)("div",{children:["Showing ",(x-1)*25+1," – ",t," of ",c.length]}),(0,l.jsxs)("div",{className:"flex",children:[(0,l.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:1===x,onClick:()=>p(x-1),children:"← Prev"}),(0,l.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",disabled:x===e,onClick:()=>p(x+1),children:"Next →"})]})]})}()]})})},eb=s(8510),ev=e=>{let{teams:t,searchParams:s,accessToken:n,setTeams:a,userID:i,userRole:d}=e,[h]=L.Z.useForm(),[m]=L.Z.useForm(),{Title:u,Paragraph:x}=ea.default,[p,j]=(0,r.useState)(""),[g,w]=(0,r.useState)(t?t[0]:null),[Z,f]=(0,r.useState)(!1),[k,_]=(0,r.useState)(!1),[b,v]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{if(null===i||null===d)return;if(null!==n){let e=(await y(n,i,d)).data.map(e=>e.id);console.log("available_model_names:",e),v(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,i,d]);let S=async e=>{try{if(null!=n){let s=await C(n,e);null!==t?a([...t,s]):a([s]),console.log("response for team create call: ".concat(s)),c.ZP.success("Team created"),f(!1)}}catch(e){console.error("Error creating the key:",e),c.ZP.error("Error creating the team: "+e)}},N=async e=>{try{if(null!=n&&null!=t){c.ZP.info("Making API Call");let s={role:"user",user_email:e.user_email,user_id:e.user_id},l=await T(n,g.team_id,s);console.log("response for team create call: ".concat(l.data));let r=t.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(l.data.team_id)),e.team_id===l.data.team_id));if(console.log("foundIndex: ".concat(r)),-1!==r){let e=[...t];e[r]=l.data,a(e),w(l.data)}_(!1)}}catch(e){console.error("Error creating the key:",e)}};return console.log("received teams ".concat(t)),(0,l.jsx)("div",{className:"w-full",children:(0,l.jsxs)(O.Z,{numItems:1,className:"gap-2 p-2 h-[75vh] w-full",children:[(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(u,{level:4,children:"All Teams"}),(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Team Name"}),(0,l.jsx)(Q.Z,{children:"Spend (USD)"}),(0,l.jsx)(Q.Z,{children:"Budget (USD)"}),(0,l.jsx)(Q.Z,{children:"TPM / RPM Limits"})]})}),(0,l.jsx)(H.Z,{children:t&&t.length>0?t.map(e=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.team_alias}),(0,l.jsx)(Y.Z,{children:e.spend}),(0,l.jsx)(Y.Z,{children:e.max_budget?e.max_budget:"No limit"}),(0,l.jsx)(Y.Z,{children:(0,l.jsxs)(R.Z,{children:["TPM Limit:"," ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,l.jsx)(Y.Z,{children:(0,l.jsx)(J.Z,{icon:eb.Z,size:"sm"})})]},e.team_id)):null})]})})]}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>f(!0),children:"+ Create New Team"}),(0,l.jsx)(B.Z,{title:"Create Team",visible:Z,width:800,footer:null,onOk:()=>{f(!1),h.resetFields()},onCancel:()=>{f(!1),h.resetFields()},children:(0,l.jsxs)(L.Z,{form:h,onFinish:S,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Team Name",name:"team_alias",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(U.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:b.map(e=>(0,l.jsx)(U.default.Option,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(K.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(V.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(u,{level:4,children:"Team Members"}),(0,l.jsx)(x,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),t&&t.length>0?(0,l.jsx)(eo.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(ei.Z,{value:String(t),onClick:()=>{w(e)},children:e.team_alias},t))}):(0,l.jsxs)(x,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Member Name"}),(0,l.jsx)(Q.Z,{children:"Role"}),(0,l.jsx)(Q.Z,{children:"Action"})]})}),(0,l.jsx)(H.Z,{children:g?g.members_with_roles.map((e,t)=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,l.jsx)(Y.Z,{children:e.role}),(0,l.jsx)(Y.Z,{children:(0,l.jsx)(J.Z,{icon:eb.Z,size:"sm"})})]},t)):null})]})})}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto mb-5",onClick:()=>_(!0),children:"+ Add member"}),(0,l.jsx)(B.Z,{title:"Add member",visible:k,width:800,footer:null,onOk:()=>{_(!1),m.resetFields()},onCancel:()=>{_(!1),m.resetFields()},children:(0,l.jsxs)(L.Z,{form:h,onFinish:N,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,l.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,l.jsx)(L.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(V.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},eS=e=>{let{searchParams:t,accessToken:s}=e,[n]=L.Z.useForm(),[a]=L.Z.useForm(),{Title:i,Paragraph:d}=ea.default,[h,m]=(0,r.useState)(""),[u,x]=(0,r.useState)(null),[p,j]=(0,r.useState)(!1);(0,r.useEffect)(()=>{(async()=>{if(null!=s){let e=[],t=await A(s,"proxy_admin_viewer");t.forEach(t=>{e.push({user_role:t.user_role,user_id:t.user_id,user_email:t.user_email})}),console.log("proxy viewers: ".concat(t));let l=await A(s,"proxy_admin");l.forEach(t=>{e.push({user_role:t.user_role,user_id:t.user_id,user_email:t.user_email})}),console.log("proxy admins: ".concat(l)),console.log("combinedList: ".concat(e)),x(e)}})()},[s]);let y=async e=>{try{if(null!=s&&null!=u){c.ZP.info("Making API Call"),e.user_email,e.user_id;let t=await I(s,e);console.log("response for team create call: ".concat(t));let l=u.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(t.user_id)),e.user_id===t.user_id));console.log("foundIndex: ".concat(l)),-1==l&&(console.log("updates admin with new user"),u.push(t),x(u)),j(!1)}}catch(e){console.error("Error creating the key:",e)}};return console.log("admins: ".concat(null==u?void 0:u.length)),(0,l.jsxs)("div",{className:"w-full m-2",children:[(0,l.jsx)(i,{level:4,children:"Restricted Access"}),(0,l.jsxs)(d,{children:["Add other people to just view spend. They cannot create keys, teams or grant users access to new models."," ",(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"})]}),(0,l.jsxs)(O.Z,{numItems:1,className:"gap-2 p-0 w-full",children:[(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Member Name"}),(0,l.jsx)(Q.Z,{children:"Role"}),(0,l.jsx)(Q.Z,{children:"Action"})]})}),(0,l.jsx)(H.Z,{children:u?u.map((e,t)=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,l.jsx)(Y.Z,{children:e.user_role}),(0,l.jsx)(Y.Z,{children:(0,l.jsx)(J.Z,{icon:eb.Z,size:"sm"})})]},t)):null})]})})}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto mb-5",onClick:()=>j(!0),children:"+ Add viewer"}),(0,l.jsx)(B.Z,{title:"Add viewer",visible:p,width:800,footer:null,onOk:()=>{j(!1),a.resetFields()},onCancel:()=>{j(!1),a.resetFields()},children:(0,l.jsxs)(L.Z,{form:n,onFinish:y,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,l.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,l.jsx)(L.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(V.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})]})},eN=s(12968),eA=s(67951);async function eC(e,t,s,l){console.log("isLocal:",!1);let r=window.location.origin,n=new eN.ZP.OpenAI({apiKey:l,baseURL:r,dangerouslyAllowBrowser:!0});for await(let l of(await n.chat.completions.create({model:s,stream:!0,messages:[{role:"user",content:e}]})))console.log(l),l.choices[0].delta.content&&t(l.choices[0].delta.content)}var eT=e=>{let{accessToken:t,token:s,userRole:n,userID:a}=e,[o,i]=(0,r.useState)(""),[c,d]=(0,r.useState)([]),[h,m]=(0,r.useState)(void 0),[u,x]=(0,r.useState)(null);(0,r.useEffect)(()=>{t&&s&&n&&a&&(async()=>{let e=await y(t,a,n);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(x(e.data),m(e.data[0].id))})()},[t,a,n]);let p=(e,t)=>{d(s=>{let l=s[s.length-1];return l&&l.role===e?[...s.slice(0,s.length-1),{role:e,content:l.content+t}]:[...s,{role:e,content:t}]})},j=async()=>{if(""!==o.trim()&&t&&s&&n&&a){d(e=>[...e,{role:"user",content:o}]);try{h&&await eC(o,e=>p("assistant",e),h,t)}catch(e){console.error("Error fetching model response",e),p("assistant","Error fetching model response")}i("")}};if(n&&"Admin Viewer"==n){let{Title:e,Paragraph:t}=ea.default;return(0,l.jsxs)("div",{children:[(0,l.jsx)(e,{level:1,children:"Access Denied"}),(0,l.jsx)(t,{children:"Ask your proxy admin for access to test models"})]})}return(0,l.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,l.jsx)(O.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,l.jsx)(M.Z,{children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{className:"mt-4",children:[(0,l.jsx)(ej.Z,{children:"Chat"}),(0,l.jsx)(ej.Z,{children:"API Reference"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsxs)(ew.Z,{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{children:"Select Model:"}),(0,l.jsx)("select",{value:h||"",onChange:e=>m(e.target.value),children:null==u?void 0:u.map(e=>(0,l.jsx)("option",{value:e.id,children:e.id},e.id))})]}),(0,l.jsxs)($.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,l.jsx)(X.Z,{children:(0,l.jsx)(ee.Z,{children:(0,l.jsx)(Y.Z,{children:(0,l.jsx)(D.Z,{children:"Chat"})})})}),(0,l.jsx)(H.Z,{children:c.map((e,t)=>(0,l.jsx)(ee.Z,{children:(0,l.jsx)(Y.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,l.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,l.jsxs)("div",{className:"flex",children:[(0,l.jsx)("input",{type:"text",value:o,onChange:e=>i(e.target.value),className:"flex-1 p-2 border rounded-md mr-2",placeholder:"Type your message..."}),(0,l.jsx)("button",{onClick:j,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,l.jsx)(ew.Z,{children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{children:[(0,l.jsx)(ej.Z,{children:"OpenAI Python SDK"}),(0,l.jsx)(ej.Z,{children:"LlamaIndex"}),(0,l.jsx)(ej.Z,{children:"Langchain Py"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsx)(ew.Z,{children:(0,l.jsx)(eA.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="http://0.0.0.0:4000" # proxy base url\n)\n\nresponse = client.chat.completions.create(\n model="gpt-3.5-turbo", # model to use from Models Tab\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ],\n extra_body={\n "metadata": {\n "generation_name": "ishaan-generation-openai-client",\n "generation_id": "openai-client-gen-id22",\n "trace_id": "openai-client-trace-id22",\n "trace_user_id": "openai-client-user-id2"\n }\n }\n)\n\nprint(response)\n '})}),(0,l.jsx)(ew.Z,{children:(0,l.jsx)(eA.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="azure-gpt-3.5", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="http://0.0.0.0:4000",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n '})}),(0,l.jsx)(ew.Z,{children:(0,l.jsx)(eA.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="http://0.0.0.0:8000",\n model = "gpt-3.5-turbo",\n temperature=0.1,\n extra_body={\n "metadata": {\n "generation_name": "ishaan-generation-langchain-client",\n "generation_id": "langchain-client-gen-id22",\n "trace_id": "langchain-client-trace-id22",\n "trace_user_id": "langchain-client-user-id2"\n }\n }\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n '})})]})]})})]})]})})})})},eI=s(33509),eE=s(30569);let{Sider:eP}=eI.default;var eF=e=>{let{setPage:t,userRole:s,defaultSelectedKey:r}=e;return"Admin Viewer"==s?(0,l.jsx)(eI.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,l.jsx)(eP,{width:120,children:(0,l.jsxs)(eE.Z,{mode:"inline",defaultSelectedKeys:r||["4"],style:{height:"100%",borderRight:0},children:[(0,l.jsx)(eE.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"4"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"1")]})})}):(0,l.jsx)(eI.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,l.jsx)(eP,{width:120,children:(0,l.jsxs)(eE.Z,{mode:"inline",defaultSelectedKeys:r||["1"],style:{height:"100%",borderRight:0},children:[(0,l.jsx)(eE.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4"),"Admin"==s?(0,l.jsx)(eE.Z.Item,{onClick:()=>t("users"),children:"Users"},"5"):null,"Admin"==s?(0,l.jsx)(eE.Z.Item,{onClick:()=>t("teams"),children:"Teams"},"6"):null,"Admin"==s?(0,l.jsx)(eE.Z.Item,{onClick:()=>t("admin-panel"),children:"Admin"},"7"):null]})})})},eO=e=>{let{accessToken:t,token:s,userRole:n,userID:a}=e,o=new Date,[i,c]=(0,r.useState)([]),[d,h]=(0,r.useState)([]),[m,u]=(0,r.useState)([]),[x,p]=(0,r.useState)([]),[j,y]=(0,r.useState)([]),[g,_]=(0,r.useState)([]),[S,N]=(0,r.useState)([]),A=new Date(o.getFullYear(),o.getMonth(),1),C=new Date(o.getFullYear(),o.getMonth()+1,0),T=E(A),I=E(C);function E(e){let t=e.getFullYear(),s=e.getMonth()+1,l=e.getDate();return"".concat(t,"-").concat(s<10?"0"+s:s,"-").concat(l<10?"0"+l:l)}return console.log("Start date is ".concat(T)),console.log("End date is ".concat(I)),(0,r.useEffect)(()=>{t&&s&&n&&a&&(async()=>{try{if(console.log("user role: ".concat(n)),"Admin"==n||"Admin Viewer"==n){let e=await f(t);c(e);let s=(await k(t)).map(e=>({key:(e.key_name||e.key_alias||e.api_key).substring(0,7),spend:e.total_spend}));h(s);let l=(await b(t)).map(e=>({key:e.model,spend:e.total_spend}));u(l);let r=await w(t);console.log("teamSpend",r),y(r.daily_spend),_(r.teams),N(r.total_spend_per_team)}else"App Owner"==n&&await Z(t,s,n,a,T,I).then(async e=>{if(console.log("result from spend logs call",e),"daily_spend"in e){let t=e.daily_spend;console.log("daily spend",t),c(t);let s=e.top_api_keys;h(s)}else{let s=(await v(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[s,l]=e;"spend"!==s&&"startTime"!==s&&"models"!==s&&"users"!==s&&t.push({key:s,spend:l})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let s=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(s[0]))),s}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));h(s),p(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[s,l]=e;""!==s&&null!=s&&"None"!=s&&(t[s]||(t[s]=0),t[s]+=l)})});let s=Object.entries(t).map(e=>{let[t,s]=e;return{user_id:t,spend:s}});s.sort((e,t)=>t.spend-e.spend);let l=s.slice(0,5);return console.log("topKeys: ".concat(Object.values(l[0]))),l}(e)),c(e)}})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,s,n,a,T,I]),(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{className:"mt-4",children:[(0,l.jsx)(ej.Z,{children:"All Up"}),(0,l.jsx)(ej.Z,{children:"Team Based Usage"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsx)(ew.Z,{children:(0,l.jsxs)(O.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(F.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Monthly Spend"}),(0,l.jsx)(et.Z,{data:i,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Top API Keys"}),(0,l.jsx)(et.Z,{className:"mt-4 h-40",data:d,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Top Users"}),(0,l.jsx)(et.Z,{className:"mt-4 h-40",data:x,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Top Models"}),(0,l.jsx)(et.Z,{className:"mt-4 h-40",data:m,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})}),(0,l.jsx)(ew.Z,{children:(0,l.jsxs)(O.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(F.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Daily Spend Per Team"}),(0,l.jsx)(et.Z,{className:"h-72",data:j,index:"date",categories:g,yAxisWidth:30,stack:!0})]})}),(0,l.jsx)(F.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Total Spend Per Team"}),(0,l.jsx)(et.Z,{className:"h-72",data:S,index:"team_id",categories:["total_spend"],yAxisWidth:30})]})})]})})]})]})})},eM=()=>{let{Title:e,Paragraph:t}=ea.default,[s,a]=(0,r.useState)(""),[o,c]=(0,r.useState)(null),[d,h]=(0,r.useState)(null),[m,u]=(0,r.useState)(null),[x,p]=(0,r.useState)(!0),j=(0,n.useSearchParams)(),y=j.get("userID"),g=j.get("token"),[w,Z]=(0,r.useState)("api-keys"),[f,k]=(0,r.useState)(null);return(0,r.useEffect)(()=>{if(g){let e=(0,ed.o)(g);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),k(e.key),e.user_role){let t=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e.toLowerCase())),console.log("Received user role length: ".concat(e.toLowerCase().length)),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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t),"Admin Viewer"==t&&Z("usage")}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?p("username_password"==e.login_method):console.log("User Email is not set ".concat(e))}}},[g]),(0,l.jsx)(r.Suspense,{fallback:(0,l.jsx)("div",{children:"Loading..."}),children:(0,l.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,l.jsx)(i,{userID:y,userRole:s,userEmail:o,showSSOBanner:x}),(0,l.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,l.jsx)(eF,{setPage:Z,userRole:s,defaultSelectedKey:null}),"api-keys"==w?(0,l.jsx)(eh,{userID:y,userRole:s,teams:d,keys:m,setUserRole:a,userEmail:o,setUserEmail:c,setTeams:h,setKeys:u}):"models"==w?(0,l.jsx)(ep,{userID:y,userRole:s,token:g,accessToken:f}):"llm-playground"==w?(0,l.jsx)(eT,{userID:y,userRole:s,token:g,accessToken:f}):"users"==w?(0,l.jsx)(e_,{userID:y,userRole:s,token:g,keys:m,accessToken:f,setKeys:u}):"teams"==w?(0,l.jsx)(ev,{teams:d,setTeams:h,searchParams:j,accessToken:f,userID:y,userRole:s}):"admin-panel"==w?(0,l.jsx)(eS,{setTeams:h,searchParams:j,accessToken:f}):(0,l.jsx)(eO,{userID:y,userRole:s,token:g,accessToken:f})]})]})})}}},function(e){e.O(0,[730,971,69,744],function(){return e(e.s=79615)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-8672e345e79b8043.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-8672e345e79b8043.js new file mode 100644 index 000000000..d196ba33f --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-8672e345e79b8043.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,s){Promise.resolve().then(s.bind(s,92182))},92182:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return eM}});var l=s(3827),r=s(64090),a=s(47907),n=s(8792),o=s(2179),i=e=>{let{userID:t,userRole:s,userEmail:r,showSSOBanner:a}=e;return console.log("User ID:",t),console.log("userEmail:",r),(0,l.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,l.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,l.jsx)("div",{className:"flex flex-col items-center",children:(0,l.jsx)(n.default,{href:"/",children:(0,l.jsx)("button",{className:"text-gray-800 text-2xl py-1 rounded text-center",children:(0,l.jsx)("img",{src:"/get_image",width:200,height:200,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,l.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[a?(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#setup-ssoauth-for-ui",target:"_blank",className:"mr-2"}):null,(0,l.jsxs)(o.Z,{variant:"secondary",size:"lg",children:[r,(0,l.jsxs)("p",{children:["Role: ",s]}),(0,l.jsxs)("p",{children:["ID: ",t]})]})]})]})},c=s(80588);let d=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await l.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/user/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await l.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t);let s=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},u=async function(e,t,s){let l=arguments.length>3&&void 0!==arguments[3]&&arguments[3];try{let r="/user/info";"App Owner"==s&&t&&(r="".concat(r,"?user_id=").concat(t)),console.log("in userInfoCall viewAll=",l),l&&(r="".concat(r,"?view_all=true"));let a=await fetch(r,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw c.ZP.error(e),Error("Network response was not ok")}let n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async e=>{try{let t=await fetch("/global/spend",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,t,s)=>{try{let t=await fetch("/v2/model/info",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t,s)=>{try{let t=await fetch("/model/metrics",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,t,s)=>{try{let t=await fetch("/models",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let s="/global/spend/logs";console.log("in keySpendLogsCall:",s);let l=await fetch("".concat(s,"?api_key=").concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let r=await l.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},w=async e=>{try{let t="/global/spend/teams";console.log("in teamSpendLogsCall:",t);let s=await fetch("".concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},Z=async(e,t,s,l,r,a)=>{try{console.log("user role in spend logs call: ".concat(s));let t="/spend/logs";t="App Owner"==s?"".concat(t,"?user_id=").concat(l,"&start_date=").concat(r,"&end_date=").concat(a):"".concat(t,"?start_date=").concat(r,"&end_date=").concat(a);let n=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let o=await n.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},f=async e=>{try{let t=await fetch("/global/spend/logs",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},k=async e=>{try{let t=await fetch("/global/spend/keys?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,t)=>{try{t&&JSON.stringify({api_key:t});let s={method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}};t&&(s.body=JSON.stringify({api_key:t}));let l=await fetch("/global/spend/end_users",s);if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let r=await l.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},b=async e=>{try{let t=await fetch("/global/spend/models?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},v=async(e,t)=>{try{let s=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},S=async(e,t,s,l)=>{try{let r=await fetch("/user/request_model",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({models:[t],user_id:s,justification:l})});if(!r.ok){let e=await r.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let a=await r.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},N=async e=>{try{let t="/user/get_requests";console.log("in userGetRequesedtModelsCall:",t);let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to get requested models:",e),e}},A=async(e,t)=>{try{let s="/user/get_users?role=".concat(t);console.log("in userGetAllUsersCall:",s);let l=await fetch(s,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let r=await l.json();return console.log(r),r}catch(e){throw console.error("Failed to get requested models:",e),e}},C=async(e,t)=>{try{console.log("Form Values in teamCreateCall:",t);let s=await fetch("/team/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let l=await s.json();return console.log("API Response:",l),l}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,t,s)=>{try{console.log("Form Values in teamMemberAddCall:",s);let l=await fetch("/team/member_add",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await l.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},I=async(e,t)=>{try{console.log("Form Values in userUpdateUserCall:",t);let s=await fetch("/user/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_role:"proxy_admin_viewer",...t})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let l=await s.json();return console.log("API Response:",l),l}catch(e){throw console.error("Failed to create key:",e),e}},E=async(e,t)=>{try{let s=await fetch("/global/predict/spend/logs",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({data:t})});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},P=async e=>{try{console.log("Checking Slack Budget Alerts service health");let t=await fetch("/health/services?service=slack_budget_alerts",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error("Failed Slack Alert test: "+e),Error(e)}let s=await t.json();return c.ZP.success("Test Slack Alert worked - check your Slack!"),console.log("Service Health Response:",s),s}catch(e){throw console.error("Failed to perform health check:",e),e}};var F=s(10384),O=s(46453),M=s(13810),R=s(71801),D=s(42440),U=s(17189),L=s(12143),B=s(77171),z=s(42539),K=s(88707),W=s(1861);let{Option:V}=U.default;var q=e=>{let{userID:t,team:s,userRole:a,accessToken:n,data:i,setData:h}=e,[m]=L.Z.useForm(),[u,x]=(0,r.useState)(!1),[p,j]=(0,r.useState)(null),[g,w]=(0,r.useState)(null),[Z,f]=(0,r.useState)([]),k=()=>{x(!1),m.resetFields()},_=()=>{x(!1),j(null),m.resetFields()};(0,r.useEffect)(()=>{(async()=>{try{if(null===t||null===a)return;if(null!==n){let e=(await y(n,t,a)).data.map(e=>e.id);console.log("available_model_names:",e),f(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,t,a]);let b=async e=>{try{c.ZP.info("Making API Call"),x(!0);let s=await d(n,t,e);console.log("key create Response:",s),h(e=>e?[...e,s]:[s]),j(s.key),w(s.soft_budget),c.ZP.success("API Key Created"),m.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}},v=async()=>{try{console.log("Sending Slack alert...");let e=await P(n);console.log("slackBudgetAlertsHealthCheck Response:",e),console.log("Testing Slack alert successful")}catch(e){console.error("Error sending Slack alert:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,l.jsx)(B.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:k,onCancel:_,children:(0,l.jsxs)(L.Z,{form:m,onFinish:b,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===a||"Admin"===a?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Team ID",name:"team_id",initialValue:s?s.team_id:null,valuePropName:"team_id",children:(0,l.jsx)(z.Z,{value:s?s.team_alias:"",disabled:!0})}),(0,l.jsx)(L.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(U.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:Z.map(e=>(0,l.jsx)(V,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Soft Budget (USD) Monthly",name:"soft_budget",initialValue:50,children:(0,l.jsx)(K.Z,{step:.01,precision:2,defaultValue:50,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(K.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Reset Budget",name:"budget_duration",children:(0,l.jsxs)(U.default,{defaultValue:null,placeholder:"n/a",children:[(0,l.jsx)(U.default.Option,{value:"24h",children:"daily"}),(0,l.jsx)(U.default.Option,{value:"30d",children:"monthly"})]})}),(0,l.jsx)(L.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(z.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,l.jsx)(z.Z,{placeholder:"default team (create a new team)"})}),(0,l.jsx)(L.Z.Item,{label:"Description",name:"description",children:(0,l.jsx)(z.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,l.jsx)(B.Z,{visible:u,onOk:k,onCancel:_,footer:null,children:(0,l.jsx)(O.Z,{numItems:1,className:"gap-2 w-full",children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Save your Key"}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,l.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,l.jsx)(F.Z,{numColSpan:1,children:null!=p?(0,l.jsxs)("div",{children:[(0,l.jsxs)(R.Z,{children:["API Key: ",p]}),(0,l.jsx)(D.Z,{className:"mt-6",children:"Budgets"}),(0,l.jsxs)(R.Z,{children:["Soft Limit Budget: $",g]}),(0,l.jsx)(o.Z,{className:"mt-3",onClick:v,children:"Test Slack Alert"}),(0,l.jsxs)(R.Z,{className:"mt-2",children:["(LiteLLM Docs -",(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/alerting",target:"_blank",className:"text-blue-500",children:"Set Up Slack Alerting)"})]})]}):(0,l.jsx)(R.Z,{children:"Key being created, this might take 30s"})})]})})})]})},J=s(33393),G=s(61244),$=s(10827),H=s(3851),Y=s(2044),X=s(64167),Q=s(74480),ee=s(7178),et=s(9853),es=s(56863),el=e=>{let{token:t,accessToken:s,keySpend:a,keyBudget:n,keyName:i}=e,[c,d]=(0,r.useState)(!1),[h,m]=(0,r.useState)(null),[u,x]=(0,r.useState)(""),[p,j]=(0,r.useState)(null),y=async()=>{try{if(null==s||null==t)return;console.log("accessToken: ".concat(s,"; token: ").concat(t));let e=await g(s,t);console.log("Response:",e),m(e);let l=await E(s,e);console.log("Response2:",l);let r=[...e,...l.response];m(r),x(l.predicted_spend),console.log("Combined Data:",r)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{size:"xs",onClick:()=>{console.log("Show Modal triggered"),d(!0),y()},variant:"secondary",children:"Spend Report"}),(0,l.jsxs)(B.Z,{visible:c,width:1400,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,l.jsxs)(D.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,l.jsxs)(es.Z,{children:["Monthly Spend $",a]}),(0,l.jsx)(D.Z,{children:u}),(0,l.jsx)(M.Z,{className:"mt-6 mb-6",children:h&&(0,l.jsx)(et.Z,{className:"mt-6",data:h,colors:["blue","amber"],index:"date",categories:["spend","predicted_spend"],yAxisWidth:80})})]})]}):null},er=e=>{let{userID:t,accessToken:s,data:a,setData:n}=e,[i,c]=(0,r.useState)(!1),[d,h]=(0,r.useState)(!1),[u,x]=(0,r.useState)(null),p=async e=>{null!=a&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=a){try{await m(s,u);let e=a.filter(e=>e.token!==u);n(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=a)return console.log("RERENDER TRIGGERED"),(0,l.jsxs)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,l.jsxs)($.Z,{className:"mt-5",children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Key Alias"}),(0,l.jsx)(Q.Z,{children:"Secret Key"}),(0,l.jsx)(Q.Z,{children:"Spend (USD)"}),(0,l.jsx)(Q.Z,{children:"Key Budget (USD)"}),(0,l.jsx)(Q.Z,{children:"Spend Report"}),(0,l.jsx)(Q.Z,{children:"Team ID"}),(0,l.jsx)(Q.Z,{children:"Metadata"}),(0,l.jsx)(Q.Z,{children:"Models"}),(0,l.jsx)(Q.Z,{children:"TPM / RPM Limits"}),(0,l.jsx)(Q.Z,{children:"Expires"})]})}),(0,l.jsx)(H.Z,{children:a.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,l.jsx)(R.Z,{children:e.key_alias}):(0,l.jsx)(R.Z,{children:"Not Set"})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:e.key_name})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(t){return e.spend}})()})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.max_budget?(0,l.jsx)(R.Z,{children:e.max_budget}):(0,l.jsx)(R.Z,{children:"Unlimited Budget"})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px"},children:(0,l.jsx)(el,{token:e.token,accessToken:s,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:e.team_alias&&"None"!=e.team_alias?e.team_alias:e.team_id})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:JSON.stringify(e.metadata).slice(0,400)})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:JSON.stringify(e.models)})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",overflowWrap:"break-word"},children:(0,l.jsxs)(R.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",wordWrap:"break-word"},children:null!=e.expires?(0,l.jsx)(R.Z,{children:e.expires}):(0,l.jsx)(R.Z,{children:"Never"})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",wordWrap:"break-word"},children:(0,l.jsx)(G.Z,{onClick:()=>p(e.token),icon:J.Z,size:"sm"})})]},e.token))})]}),d&&(0,l.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,l.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,l.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,l.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,l.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,l.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,l.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,l.jsx)("div",{className:"sm:flex sm:items-start",children:(0,l.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,l.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,l.jsx)("div",{className:"mt-2",children:(0,l.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,l.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,l.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,l.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},ea=e=>{let{userID:t,userSpendData:s,userRole:a,accessToken:n}=e,[o,i]=(0,r.useState)(null==s?void 0:s.spend),[c,d]=(0,r.useState)((null==s?void 0:s.max_budget)||null);(0,r.useEffect)(()=>{(async()=>{if("Admin"===a)try{let e=await x(n);i(e.spend),d(e.max_budget||null)}catch(e){console.error("Error fetching global spend data:",e)}})()},[a,n]);let h=void 0!==o?o.toFixed(4):null;return(0,l.jsx)(l.Fragment,{children:(0,l.jsxs)(M.Z,{className:"mx-auto mb-4",children:[(0,l.jsxs)(es.Z,{children:["$",h]}),(0,l.jsxs)(D.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},en=s(36083),eo=s(68967),ei=s(27166),ec=e=>{let{teams:t,setSelectedTeam:s}=e,{Title:a,Paragraph:n}=en.default,[o,i]=(0,r.useState)("");return(0,l.jsxs)("div",{className:"mt-10",children:[(0,l.jsx)(a,{level:4,children:"Default Team"}),(0,l.jsx)(n,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),t&&t.length>0?(0,l.jsx)(eo.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(ei.Z,{value:String(t),onClick:()=>s(e),children:e.team_alias},t))}):(0,l.jsxs)(n,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]})},ed=s(37963);console.log("isLocal:",!1);var eh=e=>{let{userID:t,userRole:s,teams:n,keys:o,setUserRole:i,userEmail:c,setUserEmail:d,setTeams:h,setKeys:m}=e,[p,j]=(0,r.useState)(null),g=(0,a.useSearchParams)();g.get("viewSpend"),(0,a.useRouter)();let w=g.get("token"),[Z,f]=(0,r.useState)(null),[k,_]=(0,r.useState)([]),[b,v]=(0,r.useState)(n?n[0]:null);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,r.useEffect)(()=>{if(w){let e=(0,ed.o)(w);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),f(e.key),e.user_role){let t=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e)),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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),i(t)}else console.log("User role not defined");e.user_email?d(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&Z&&s&&!o&&!p){let e=sessionStorage.getItem("userModels"+t);e?_(JSON.parse(e)):(async()=>{try{let e=await u(Z,t,s);if(console.log("received teams in user dashboard: ".concat(Object.keys(e),"; team values: ").concat(Object.entries(e.teams))),"Admin"==s){let e=await x(Z);j(e),console.log("globalSpend:",e)}else j(e.user_info);m(e.keys),h(e.teams),v(e.teams?e.teams[0]:null),sessionStorage.setItem("userData"+t,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let l=(await y(Z,t,s)).data.map(e=>e.id);console.log("available_model_names:",l),_(l),console.log("userModels:",k),sessionStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,w,Z,o,s]),null==t||null==w){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}if(null==Z)return null;if(null==s&&i("App Owner"),s&&"Admin Viewer"==s){let{Title:e,Paragraph:t}=en.default;return(0,l.jsxs)("div",{children:[(0,l.jsx)(e,{level:1,children:"Access Denied"}),(0,l.jsx)(t,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",b),(0,l.jsx)("div",{children:(0,l.jsx)(O.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(ea,{userID:t,userSpendData:p,userRole:s,accessToken:Z}),(0,l.jsx)(er,{userID:t,accessToken:Z,data:o,setData:m}),(0,l.jsx)(q,{userID:t,team:b||null,userRole:s,accessToken:Z,data:o,setData:m},b?b.team_id:null),(0,l.jsx)(ec,{teams:n,setSelectedTeam:v})]})})})},em=s(5);let{Option:eu}=U.default;var ex=e=>{let{userModels:t,accessToken:s,userID:a}=e,[n]=L.Z.useForm(),[i,d]=(0,r.useState)(!1),h=async e=>{try{c.ZP.info("Requesting access");let{selectedModel:t,accessReason:l}=e;await S(s,t,a,l),d(!0)}catch(e){console.error("Error requesting access:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{size:"xs",onClick:()=>d(!0),children:"Request Access"}),(0,l.jsx)(B.Z,{title:"Request Access",visible:i,width:800,footer:null,onOk:()=>{d(!1),n.resetFields()},onCancel:()=>{d(!1),n.resetFields()},children:(0,l.jsxs)(L.Z,{form:n,onFinish:h,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(L.Z.Item,{label:"Select Model",name:"selectedModel",children:(0,l.jsx)(U.default,{placeholder:"Select model",style:{width:"100%"},children:t.map(e=>(0,l.jsx)(eu,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Reason for Access",name:"accessReason",children:(0,l.jsx)(z.Z.TextArea,{rows:4,placeholder:"Enter reason for access"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(o.Z,{children:"Request Access"})})]})})]})},ep=e=>{let{accessToken:t,token:s,userRole:a,userID:n}=e,[o,i]=(0,r.useState)({data:[]}),[c,d]=(0,r.useState)([]),[h,m]=(0,r.useState)([]);if((0,r.useEffect)(()=>{if(!t||!s||!a||!n)return;let e=async()=>{try{let e=await p(t,n,a);console.log("Model data response:",e.data),i(e);let s=await j(t,n,a);if(console.log("Model metrics response:",s),d(s),"Admin"===a&&t){let e=await N(t);console.log("Pending Requests:",h),m(e.requests||[])}}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&a&&n&&e()},[t,s,a,n]),!o||!t||!s||!a||!n)return(0,l.jsx)("div",{children:"Loading..."});let u=[];for(let e=0;e(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:(0,l.jsx)(D.Z,{children:e.model_name})}),(0,l.jsx)(Y.Z,{children:e.provider}),"Admin"===a&&(0,l.jsx)(Y.Z,{children:e.api_base}),(0,l.jsx)(Y.Z,{children:e.user_access?(0,l.jsx)(em.Z,{color:"green",children:"Yes"}):(0,l.jsx)(ex,{userModels:u,accessToken:t,userID:n})}),(0,l.jsx)(Y.Z,{children:e.input_cost}),(0,l.jsx)(Y.Z,{children:e.output_cost}),(0,l.jsx)(Y.Z,{children:e.max_tokens})]},e.model_name))})]})}),(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Model Statistics (Number Requests)"}),(0,l.jsx)(et.Z,{data:c,index:"model",categories:["num_requests"],colors:["blue"],yAxisWidth:400,layout:"vertical",tickGap:5})]}),(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Model Statistics (Latency)"}),(0,l.jsx)(et.Z,{data:c,index:"model",categories:["avg_latency_seconds"],colors:["red"],yAxisWidth:400,layout:"vertical",tickGap:5})]})]})})},ej=s(92836),ey=s(26734),eg=s(41608),ew=s(32126),eZ=s(23682);let{Option:ef}=U.default;var ek=e=>{let{userID:t,accessToken:s}=e,[a]=L.Z.useForm(),[n,i]=(0,r.useState)(!1),[d,m]=(0,r.useState)(null),[u,x]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{let e=await y(s,t,"any"),l=[];for(let t=0;t{i(!1),a.resetFields()},j=()=>{i(!1),m(null),a.resetFields()},g=async e=>{try{c.ZP.info("Making API Call"),i(!0),console.log("formValues in create user:",e);let l=await h(s,t,e);console.log("user create Response:",l),m(l.key),c.ZP.success("API user Created"),a.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the user:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>i(!0),children:"+ Create New User"}),(0,l.jsx)(B.Z,{title:"Create User",visible:n,width:800,footer:null,onOk:p,onCancel:j,children:(0,l.jsxs)(L.Z,{form:a,onFinish:g,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(L.Z.Item,{label:"User ID",name:"user_id",children:(0,l.jsx)(z.Z,{placeholder:"Enter User ID"})}),(0,l.jsx)(L.Z.Item,{label:"Team ID",name:"team_id",children:(0,l.jsx)(z.Z,{placeholder:"ai_team"})}),(0,l.jsx)(L.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(U.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:u.map(e=>(0,l.jsx)(ef,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(K.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(z.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Create User"})})]})}),d&&(0,l.jsxs)(B.Z,{title:"Save Your User",visible:n,onOk:p,onCancel:j,footer:null,children:[(0,l.jsxs)("p",{children:["Please save this secret user somewhere safe and accessible. For security reasons, ",(0,l.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret user, you will need to generate a new one."]}),(0,l.jsx)("p",{children:null!=d?"API user: ".concat(d):"User being created, this might take 30s"})]})]})},e_=e=>{let{accessToken:t,token:s,keys:a,userRole:n,userID:o,setKeys:i}=e,[c,d]=(0,r.useState)(null),[h,m]=(0,r.useState)(null),[x,p]=(0,r.useState)(1);if((0,r.useEffect)(()=>{if(!t||!s||!n||!o)return;let e=async()=>{try{let e=await u(t,null,n,!0);console.log("user data response:",e),d(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&n&&o&&!c&&e();let l=async()=>{try{let e=await _(t,null);console.log("user data response:",e),m(e)}catch(e){console.error("There was an error fetching the model data",e)}};n&&("Admin"==n||"Admin Viewer"==n)&&!h&&l()},[t,s,n,o]),!c||!t||!s||!n||!o)return(0,l.jsx)("div",{children:"Loading..."});let j=async e=>{try{let s=await _(t,e);console.log("user data response:",s),m(s)}catch(e){console.error("There was an error fetching the model data",e)}};return(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(O.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(ek,{userID:o,accessToken:t}),(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{variant:"line",defaultValue:"1",children:[(0,l.jsx)(ej.Z,{value:"1",children:"Key Owners"}),(0,l.jsx)(ej.Z,{value:"2",children:"End-Users"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsx)(ew.Z,{children:(0,l.jsxs)($.Z,{className:"mt-5",children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"User ID"}),(0,l.jsx)(Q.Z,{children:"User Role"}),(0,l.jsx)(Q.Z,{children:"User Models"}),(0,l.jsx)(Q.Z,{children:"User Spend ($ USD)"}),(0,l.jsx)(Q.Z,{children:"User Max Budget ($ USD)"})]})}),(0,l.jsx)(H.Z,{children:c.map(e=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.user_id}),(0,l.jsx)(Y.Z,{children:e.user_role?e.user_role:"app_owner"}),(0,l.jsx)(Y.Z,{children:e.models&&e.models.length>0?e.models:"All Models"}),(0,l.jsx)(Y.Z,{children:e.spend?e.spend:0}),(0,l.jsx)(Y.Z,{children:e.max_budget?e.max_budget:"Unlimited"})]},e.user_id))})]})}),(0,l.jsxs)(ew.Z,{children:[(0,l.jsxs)("div",{className:"flex items-center",children:[(0,l.jsx)("div",{className:"flex-1"}),(0,l.jsxs)("div",{className:"flex-1 flex justify-between items-center",children:[(0,l.jsx)(R.Z,{className:"w-1/4 mr-2 text-right",children:"Key"}),(0,l.jsx)(eo.Z,{defaultValue:"1",className:"w-3/4",children:null==a?void 0:a.map((e,t)=>{if(e&&null!==e.key_name&&e.key_name.length>0)return(0,l.jsx)(ei.Z,{value:String(t),onClick:()=>j(e.token),children:e.key_name},t)})})]})]}),(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"End User"}),(0,l.jsx)(Q.Z,{children:"Spend"}),(0,l.jsx)(Q.Z,{children:"Total Events"})]})}),(0,l.jsx)(H.Z,{children:null==h?void 0:h.map((e,t)=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.end_user}),(0,l.jsx)(Y.Z,{children:e.total_spend}),(0,l.jsx)(Y.Z,{children:e.total_events})]},t))})]})]})]})]})}),function(){if(!c)return null;let e=Math.ceil(c.length/25),t=Math.min(25*x,c.length);return(0,l.jsxs)("div",{className:"flex justify-between items-center",children:[(0,l.jsxs)("div",{children:["Showing ",(x-1)*25+1," – ",t," of ",c.length]}),(0,l.jsxs)("div",{className:"flex",children:[(0,l.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:1===x,onClick:()=>p(x-1),children:"← Prev"}),(0,l.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",disabled:x===e,onClick:()=>p(x+1),children:"Next →"})]})]})}()]})})},eb=e=>{let{teams:t,searchParams:s,accessToken:a,setTeams:n,userID:i,userRole:d}=e,[h]=L.Z.useForm(),[m]=L.Z.useForm(),{Title:u,Paragraph:x}=en.default,[p,j]=(0,r.useState)(""),[g,w]=(0,r.useState)(t?t[0]:null),[Z,f]=(0,r.useState)(!1),[k,_]=(0,r.useState)(!1),[b,v]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{if(null===i||null===d)return;if(null!==a){let e=(await y(a,i,d)).data.map(e=>e.id);console.log("available_model_names:",e),v(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,i,d]);let S=async e=>{try{if(null!=a){c.ZP.info("Creating Team");let s=await C(a,e);null!==t?n([...t,s]):n([s]),console.log("response for team create call: ".concat(s)),c.ZP.success("Team created"),f(!1)}}catch(e){console.error("Error creating the key:",e),c.ZP.error("Error creating the team: "+e)}},N=async e=>{try{if(null!=a&&null!=t){c.ZP.info("Adding Member");let s={role:"user",user_email:e.user_email,user_id:e.user_id},l=await T(a,g.team_id,s);console.log("response for team create call: ".concat(l.data));let r=t.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(l.data.team_id)),e.team_id===l.data.team_id));if(console.log("foundIndex: ".concat(r)),-1!==r){let e=[...t];e[r]=l.data,n(e),w(l.data)}_(!1)}}catch(e){console.error("Error creating the key:",e)}};return console.log("received teams ".concat(t)),(0,l.jsx)("div",{className:"w-full mx-4",children:(0,l.jsxs)(O.Z,{numItems:1,className:"gap-2 p-2 h-[75vh] w-full",children:[(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(u,{level:4,children:"All Teams"}),(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Team Name"}),(0,l.jsx)(Q.Z,{children:"Spend (USD)"}),(0,l.jsx)(Q.Z,{children:"Budget (USD)"}),(0,l.jsx)(Q.Z,{children:"Models"}),(0,l.jsx)(Q.Z,{children:"TPM / RPM Limits"})]})}),(0,l.jsx)(H.Z,{children:t&&t.length>0?t.map(e=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.max_budget?e.max_budget:"No limit"}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:JSON.stringify(e.models?e.models:[])})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsxs)(R.Z,{children:["TPM Limit:"," ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{}),"RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})})]},e.team_id)):null})]})})]}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>f(!0),children:"+ Create New Team"}),(0,l.jsx)(B.Z,{title:"Create Team",visible:Z,width:800,footer:null,onOk:()=>{f(!1),h.resetFields()},onCancel:()=>{f(!1),h.resetFields()},children:(0,l.jsxs)(L.Z,{form:h,onFinish:S,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Team Name",name:"team_alias",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(U.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:b.map(e=>(0,l.jsx)(U.default.Option,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(K.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(u,{level:4,children:"Team Members"}),(0,l.jsx)(x,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),t&&t.length>0?(0,l.jsx)(eo.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(ei.Z,{value:String(t),onClick:()=>{w(e)},children:e.team_alias},t))}):(0,l.jsxs)(x,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Member Name"}),(0,l.jsx)(Q.Z,{children:"Role"})]})}),(0,l.jsx)(H.Z,{children:g?g.members_with_roles.map((e,t)=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,l.jsx)(Y.Z,{children:e.role})]},t)):null})]})})}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto mb-5",onClick:()=>_(!0),children:"+ Add member"}),(0,l.jsx)(B.Z,{title:"Add member",visible:k,width:800,footer:null,onOk:()=>{_(!1),m.resetFields()},onCancel:()=>{_(!1),m.resetFields()},children:(0,l.jsxs)(L.Z,{form:h,onFinish:N,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,l.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,l.jsx)(L.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},ev=s(8510),eS=e=>{let{searchParams:t,accessToken:s}=e,[a]=L.Z.useForm(),[n]=L.Z.useForm(),{Title:i,Paragraph:d}=en.default,[h,m]=(0,r.useState)(""),[u,x]=(0,r.useState)(null),[p,j]=(0,r.useState)(!1);(0,r.useEffect)(()=>{(async()=>{if(null!=s){let e=[],t=await A(s,"proxy_admin_viewer");t.forEach(t=>{e.push({user_role:t.user_role,user_id:t.user_id,user_email:t.user_email})}),console.log("proxy viewers: ".concat(t));let l=await A(s,"proxy_admin");l.forEach(t=>{e.push({user_role:t.user_role,user_id:t.user_id,user_email:t.user_email})}),console.log("proxy admins: ".concat(l)),console.log("combinedList: ".concat(e)),x(e)}})()},[s]);let y=async e=>{try{if(null!=s&&null!=u){c.ZP.info("Making API Call"),e.user_email,e.user_id;let t=await I(s,e);console.log("response for team create call: ".concat(t));let l=u.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(t.user_id)),e.user_id===t.user_id));console.log("foundIndex: ".concat(l)),-1==l&&(console.log("updates admin with new user"),u.push(t),x(u)),j(!1)}}catch(e){console.error("Error creating the key:",e)}};return console.log("admins: ".concat(null==u?void 0:u.length)),(0,l.jsxs)("div",{className:"w-full m-2",children:[(0,l.jsx)(i,{level:4,children:"Restricted Access"}),(0,l.jsxs)(d,{children:["Add other people to just view spend. They cannot create keys, teams or grant users access to new models."," ",(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"})]}),(0,l.jsxs)(O.Z,{numItems:1,className:"gap-2 p-0 w-full",children:[(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Member Name"}),(0,l.jsx)(Q.Z,{children:"Role"}),(0,l.jsx)(Q.Z,{children:"Action"})]})}),(0,l.jsx)(H.Z,{children:u?u.map((e,t)=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,l.jsx)(Y.Z,{children:e.user_role}),(0,l.jsx)(Y.Z,{children:(0,l.jsx)(G.Z,{icon:ev.Z,size:"sm"})})]},t)):null})]})})}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto mb-5",onClick:()=>j(!0),children:"+ Add viewer"}),(0,l.jsx)(B.Z,{title:"Add viewer",visible:p,width:800,footer:null,onOk:()=>{j(!1),n.resetFields()},onCancel:()=>{j(!1),n.resetFields()},children:(0,l.jsxs)(L.Z,{form:a,onFinish:y,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,l.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,l.jsx)(L.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})]})},eN=s(12968),eA=s(67951);async function eC(e,t,s,l){console.log("isLocal:",!1);let r=window.location.origin,a=new eN.ZP.OpenAI({apiKey:l,baseURL:r,dangerouslyAllowBrowser:!0});for await(let l of(await a.chat.completions.create({model:s,stream:!0,messages:[{role:"user",content:e}]})))console.log(l),l.choices[0].delta.content&&t(l.choices[0].delta.content)}var eT=e=>{let{accessToken:t,token:s,userRole:a,userID:n}=e,[o,i]=(0,r.useState)(""),[c,d]=(0,r.useState)([]),[h,m]=(0,r.useState)(void 0),[u,x]=(0,r.useState)(null);(0,r.useEffect)(()=>{t&&s&&a&&n&&(async()=>{let e=await y(t,n,a);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(x(e.data),m(e.data[0].id))})()},[t,n,a]);let p=(e,t)=>{d(s=>{let l=s[s.length-1];return l&&l.role===e?[...s.slice(0,s.length-1),{role:e,content:l.content+t}]:[...s,{role:e,content:t}]})},j=async()=>{if(""!==o.trim()&&t&&s&&a&&n){d(e=>[...e,{role:"user",content:o}]);try{h&&await eC(o,e=>p("assistant",e),h,t)}catch(e){console.error("Error fetching model response",e),p("assistant","Error fetching model response")}i("")}};if(a&&"Admin Viewer"==a){let{Title:e,Paragraph:t}=en.default;return(0,l.jsxs)("div",{children:[(0,l.jsx)(e,{level:1,children:"Access Denied"}),(0,l.jsx)(t,{children:"Ask your proxy admin for access to test models"})]})}return(0,l.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,l.jsx)(O.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,l.jsx)(M.Z,{children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{className:"mt-4",children:[(0,l.jsx)(ej.Z,{children:"Chat"}),(0,l.jsx)(ej.Z,{children:"API Reference"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsxs)(ew.Z,{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{children:"Select Model:"}),(0,l.jsx)("select",{value:h||"",onChange:e=>m(e.target.value),children:null==u?void 0:u.map(e=>(0,l.jsx)("option",{value:e.id,children:e.id},e.id))})]}),(0,l.jsxs)($.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,l.jsx)(X.Z,{children:(0,l.jsx)(ee.Z,{children:(0,l.jsx)(Y.Z,{children:(0,l.jsx)(D.Z,{children:"Chat"})})})}),(0,l.jsx)(H.Z,{children:c.map((e,t)=>(0,l.jsx)(ee.Z,{children:(0,l.jsx)(Y.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,l.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,l.jsxs)("div",{className:"flex",children:[(0,l.jsx)("input",{type:"text",value:o,onChange:e=>i(e.target.value),className:"flex-1 p-2 border rounded-md mr-2",placeholder:"Type your message..."}),(0,l.jsx)("button",{onClick:j,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,l.jsx)(ew.Z,{children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{children:[(0,l.jsx)(ej.Z,{children:"OpenAI Python SDK"}),(0,l.jsx)(ej.Z,{children:"LlamaIndex"}),(0,l.jsx)(ej.Z,{children:"Langchain Py"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsx)(ew.Z,{children:(0,l.jsx)(eA.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="http://0.0.0.0:4000" # proxy base url\n)\n\nresponse = client.chat.completions.create(\n model="gpt-3.5-turbo", # model to use from Models Tab\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ],\n extra_body={\n "metadata": {\n "generation_name": "ishaan-generation-openai-client",\n "generation_id": "openai-client-gen-id22",\n "trace_id": "openai-client-trace-id22",\n "trace_user_id": "openai-client-user-id2"\n }\n }\n)\n\nprint(response)\n '})}),(0,l.jsx)(ew.Z,{children:(0,l.jsx)(eA.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="azure-gpt-3.5", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="http://0.0.0.0:4000",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n '})}),(0,l.jsx)(ew.Z,{children:(0,l.jsx)(eA.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="http://0.0.0.0:8000",\n model = "gpt-3.5-turbo",\n temperature=0.1,\n extra_body={\n "metadata": {\n "generation_name": "ishaan-generation-langchain-client",\n "generation_id": "langchain-client-gen-id22",\n "trace_id": "langchain-client-trace-id22",\n "trace_user_id": "langchain-client-user-id2"\n }\n }\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n '})})]})]})})]})]})})})})},eI=s(33509),eE=s(30569);let{Sider:eP}=eI.default;var eF=e=>{let{setPage:t,userRole:s,defaultSelectedKey:r}=e;return"Admin Viewer"==s?(0,l.jsx)(eI.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,l.jsx)(eP,{width:120,children:(0,l.jsxs)(eE.Z,{mode:"inline",defaultSelectedKeys:r||["4"],style:{height:"100%",borderRight:0},children:[(0,l.jsx)(eE.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"4"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"1")]})})}):(0,l.jsx)(eI.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,l.jsx)(eP,{width:120,children:(0,l.jsxs)(eE.Z,{mode:"inline",defaultSelectedKeys:r||["1"],style:{height:"100%",borderRight:0},children:[(0,l.jsx)(eE.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4"),"Admin"==s?(0,l.jsx)(eE.Z.Item,{onClick:()=>t("users"),children:"Users"},"5"):null,"Admin"==s?(0,l.jsx)(eE.Z.Item,{onClick:()=>t("teams"),children:"Teams"},"6"):null,"Admin"==s?(0,l.jsx)(eE.Z.Item,{onClick:()=>t("admin-panel"),children:"Admin"},"7"):null]})})})},eO=e=>{let{accessToken:t,token:s,userRole:a,userID:n}=e,o=new Date,[i,c]=(0,r.useState)([]),[d,h]=(0,r.useState)([]),[m,u]=(0,r.useState)([]),[x,p]=(0,r.useState)([]),[j,y]=(0,r.useState)([]),[g,_]=(0,r.useState)([]),[S,N]=(0,r.useState)([]),A=new Date(o.getFullYear(),o.getMonth(),1),C=new Date(o.getFullYear(),o.getMonth()+1,0),T=E(A),I=E(C);function E(e){let t=e.getFullYear(),s=e.getMonth()+1,l=e.getDate();return"".concat(t,"-").concat(s<10?"0"+s:s,"-").concat(l<10?"0"+l:l)}return console.log("Start date is ".concat(T)),console.log("End date is ".concat(I)),(0,r.useEffect)(()=>{t&&s&&a&&n&&(async()=>{try{if(console.log("user role: ".concat(a)),"Admin"==a||"Admin Viewer"==a){let e=await f(t);c(e);let s=(await k(t)).map(e=>({key:(e.key_name||e.key_alias||e.api_key).substring(0,7),spend:e.total_spend}));h(s);let l=(await b(t)).map(e=>({key:e.model,spend:e.total_spend}));u(l);let r=await w(t);console.log("teamSpend",r),y(r.daily_spend),_(r.teams),N(r.total_spend_per_team)}else"App Owner"==a&&await Z(t,s,a,n,T,I).then(async e=>{if(console.log("result from spend logs call",e),"daily_spend"in e){let t=e.daily_spend;console.log("daily spend",t),c(t);let s=e.top_api_keys;h(s)}else{let s=(await v(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[s,l]=e;"spend"!==s&&"startTime"!==s&&"models"!==s&&"users"!==s&&t.push({key:s,spend:l})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let s=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(s[0]))),s}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));h(s),p(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[s,l]=e;""!==s&&null!=s&&"None"!=s&&(t[s]||(t[s]=0),t[s]+=l)})});let s=Object.entries(t).map(e=>{let[t,s]=e;return{user_id:t,spend:s}});s.sort((e,t)=>t.spend-e.spend);let l=s.slice(0,5);return console.log("topKeys: ".concat(Object.values(l[0]))),l}(e)),c(e)}})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,s,a,n,T,I]),(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{className:"mt-4",children:[(0,l.jsx)(ej.Z,{children:"All Up"}),(0,l.jsx)(ej.Z,{children:"Team Based Usage"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsx)(ew.Z,{children:(0,l.jsxs)(O.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(F.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Monthly Spend"}),(0,l.jsx)(et.Z,{data:i,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Top API Keys"}),(0,l.jsx)(et.Z,{className:"mt-4 h-40",data:d,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Top Users"}),(0,l.jsx)(et.Z,{className:"mt-4 h-40",data:x,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Top Models"}),(0,l.jsx)(et.Z,{className:"mt-4 h-40",data:m,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})}),(0,l.jsx)(ew.Z,{children:(0,l.jsxs)(O.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(F.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Daily Spend Per Team"}),(0,l.jsx)(et.Z,{className:"h-72",data:j,index:"date",categories:g,yAxisWidth:30,stack:!0})]})}),(0,l.jsx)(F.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Total Spend Per Team"}),(0,l.jsx)(et.Z,{className:"h-72",data:S,index:"team_id",categories:["total_spend"],yAxisWidth:30})]})})]})})]})]})})},eM=()=>{let{Title:e,Paragraph:t}=en.default,[s,n]=(0,r.useState)(""),[o,c]=(0,r.useState)(null),[d,h]=(0,r.useState)(null),[m,u]=(0,r.useState)(null),[x,p]=(0,r.useState)(!0),j=(0,a.useSearchParams)(),y=j.get("userID"),g=j.get("token"),[w,Z]=(0,r.useState)("api-keys"),[f,k]=(0,r.useState)(null);return(0,r.useEffect)(()=>{if(g){let e=(0,ed.o)(g);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),k(e.key),e.user_role){let t=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e.toLowerCase())),console.log("Received user role length: ".concat(e.toLowerCase().length)),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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),n(t),"Admin Viewer"==t&&Z("usage")}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?p("username_password"==e.login_method):console.log("User Email is not set ".concat(e))}}},[g]),(0,l.jsx)(r.Suspense,{fallback:(0,l.jsx)("div",{children:"Loading..."}),children:(0,l.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,l.jsx)(i,{userID:y,userRole:s,userEmail:o,showSSOBanner:x}),(0,l.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,l.jsx)(eF,{setPage:Z,userRole:s,defaultSelectedKey:null}),"api-keys"==w?(0,l.jsx)(eh,{userID:y,userRole:s,teams:d,keys:m,setUserRole:n,userEmail:o,setUserEmail:c,setTeams:h,setKeys:u}):"models"==w?(0,l.jsx)(ep,{userID:y,userRole:s,token:g,accessToken:f}):"llm-playground"==w?(0,l.jsx)(eT,{userID:y,userRole:s,token:g,accessToken:f}):"users"==w?(0,l.jsx)(e_,{userID:y,userRole:s,token:g,keys:m,accessToken:f,setKeys:u}):"teams"==w?(0,l.jsx)(eb,{teams:d,setTeams:h,searchParams:j,accessToken:f,userID:y,userRole:s}):"admin-panel"==w?(0,l.jsx)(eS,{setTeams:h,searchParams:j,accessToken:f}):(0,l.jsx)(eO,{userID:y,userRole:s,token:g,accessToken:f})]})]})})}}},function(e){e.O(0,[730,971,69,744],function(){return e(e.s=20661)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/main-app-096338c8e1915716.js b/litellm/proxy/_experimental/out/_next/static/chunks/main-app-9b4fb13a7db53edf.js similarity index 54% rename from litellm/proxy/_experimental/out/_next/static/chunks/main-app-096338c8e1915716.js rename to litellm/proxy/_experimental/out/_next/static/chunks/main-app-9b4fb13a7db53edf.js index 421ae3e2c..440df3cb3 100644 --- a/litellm/proxy/_experimental/out/_next/static/chunks/main-app-096338c8e1915716.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/main-app-9b4fb13a7db53edf.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[744],{70377:function(e,n,t){Promise.resolve().then(t.t.bind(t,47690,23)),Promise.resolve().then(t.t.bind(t,48955,23)),Promise.resolve().then(t.t.bind(t,5613,23)),Promise.resolve().then(t.t.bind(t,11902,23)),Promise.resolve().then(t.t.bind(t,31778,23)),Promise.resolve().then(t.t.bind(t,77831,23))}},function(e){var n=function(n){return e(e.s=n)};e.O(0,[971,69],function(){return n(35317),n(70377)}),_N_E=e.O()}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[744],{32028:function(e,n,t){Promise.resolve().then(t.t.bind(t,47690,23)),Promise.resolve().then(t.t.bind(t,48955,23)),Promise.resolve().then(t.t.bind(t,5613,23)),Promise.resolve().then(t.t.bind(t,11902,23)),Promise.resolve().then(t.t.bind(t,31778,23)),Promise.resolve().then(t.t.bind(t,77831,23))}},function(e){var n=function(n){return e(e.s=n)};e.O(0,[971,69],function(){return n(35317),n(32028)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/index.html b/litellm/proxy/_experimental/out/index.html index b138e2de3..09eaf59a0 100644 --- a/litellm/proxy/_experimental/out/index.html +++ b/litellm/proxy/_experimental/out/index.html @@ -1 +1 @@ -🚅 LiteLLM \ No newline at end of file +🚅 LiteLLM \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/index.txt b/litellm/proxy/_experimental/out/index.txt index d70e09bac..1eac71362 100644 --- a/litellm/proxy/_experimental/out/index.txt +++ b/litellm/proxy/_experimental/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[19914,["730","static/chunks/730-1411b729a1c79695.js","931","static/chunks/app/page-144687b251040a22.js"],""] +3:I[92182,["730","static/chunks/730-1411b729a1c79695.js","931","static/chunks/app/page-8672e345e79b8043.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["aIO8mtlEIEUTmgL8cGjve",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_c23dc8","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",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."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/68a21c6e6697f7ca.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["YrQPd5ySk25sMOIe_YoyJ",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_c23dc8","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",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."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/68a21c6e6697f7ca.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] 6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"🚅 LiteLLM"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]] 1:null diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index ed82946f5..07a24dd7e 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -14,9 +14,9 @@ model_list: # cache_params: # type: redis # callbacks: ["batch_redis_requests"] - # success_callbacks: ["langfuse"] +# # success_callbacks: ["langfuse"] general_settings: master_key: sk-1234 - # disable_spend_logs: true + disable_spend_logs: true database_url: "postgresql://neondb_owner:hz8tyUlJ5ivV@ep-cool-sunset-a5ywubeh.us-east-2.aws.neon.tech/neondb?sslmode=require" \ No newline at end of file diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 3da645da4..577231c26 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -698,6 +698,8 @@ class LiteLLM_VerificationTokenView(LiteLLM_VerificationToken): team_tpm_limit: Optional[int] = None team_rpm_limit: Optional[int] = None team_max_budget: Optional[float] = None + team_models: List = [] + team_blocked: bool = False soft_budget: Optional[float] = None team_model_aliases: Optional[Dict] = None diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 37ec2065f..5246fb94d 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -15,7 +15,7 @@ from litellm.proxy._types import ( LiteLLM_TeamTable, LiteLLMRoutes, ) -from typing import Optional, Literal +from typing import Optional, Literal, Union from litellm.proxy.utils import PrismaClient from litellm.caching import DualCache @@ -26,6 +26,8 @@ def common_checks( request_body: dict, team_object: LiteLLM_TeamTable, end_user_object: Optional[LiteLLM_EndUserTable], + general_settings: dict, + route: str, ) -> bool: """ Common checks across jwt + key-based auth. @@ -34,6 +36,7 @@ def common_checks( 2. If team can call model 3. If team is in budget 4. If end_user ('user' passed to /chat/completions, /embeddings endpoint) is in budget + 5. [OPTIONAL] If 'enforce_end_user' enabled - did developer pass in 'user' param for openai endpoints """ _model = request_body.get("model", None) if team_object.blocked == True: @@ -65,6 +68,16 @@ def common_checks( raise Exception( f"End User={end_user_object.user_id} over budget. Spend={end_user_object.spend}, Budget={end_user_budget}" ) + # 5. [OPTIONAL] If 'enforce_user_param' enabled - did developer pass in 'user' param for openai endpoints + if ( + general_settings.get("enforce_user_param", None) is not None + and general_settings["enforce_user_param"] == True + ): + if route in LiteLLMRoutes.openai_routes.value and "user" not in request_body: + raise Exception( + f"'user' param not passed in. 'enforce_user_param'={general_settings['enforce_user_param']}" + ) + return True diff --git a/litellm/proxy/auth/handle_jwt.py b/litellm/proxy/auth/handle_jwt.py index 08ffc0955..4689ffe7b 100644 --- a/litellm/proxy/auth/handle_jwt.py +++ b/litellm/proxy/auth/handle_jwt.py @@ -6,7 +6,6 @@ Currently only supports admin. JWT token must have 'litellm_proxy_admin' in scope. """ -import httpx import jwt import json import os @@ -14,42 +13,10 @@ from litellm.caching import DualCache from litellm._logging import verbose_proxy_logger from litellm.proxy._types import LiteLLM_JWTAuth, LiteLLM_UserTable from litellm.proxy.utils import PrismaClient +from litellm.llms.custom_httpx.httpx_handler import HTTPHandler from typing import Optional -class HTTPHandler: - def __init__(self, concurrent_limit=1000): - # Create a client with a connection pool - self.client = httpx.AsyncClient( - limits=httpx.Limits( - max_connections=concurrent_limit, - max_keepalive_connections=concurrent_limit, - ) - ) - - async def close(self): - # Close the client when you're done with it - await self.client.aclose() - - async def get( - self, url: str, params: Optional[dict] = None, headers: Optional[dict] = None - ): - response = await self.client.get(url, params=params, headers=headers) - return response - - async def post( - self, - url: str, - data: Optional[dict] = None, - params: Optional[dict] = None, - headers: Optional[dict] = None, - ): - response = await self.client.post( - url, data=data, params=params, headers=headers - ) - return response - - class JWTHandler: """ - treat the sub id passed in as the user id diff --git a/litellm/proxy/proxy_cli.py b/litellm/proxy/proxy_cli.py index b1d7b8026..b8d792696 100644 --- a/litellm/proxy/proxy_cli.py +++ b/litellm/proxy/proxy_cli.py @@ -21,8 +21,6 @@ telemetry = None def append_query_params(url, params): from litellm._logging import verbose_proxy_logger - from litellm._logging import verbose_proxy_logger - verbose_proxy_logger.debug(f"url: {url}") verbose_proxy_logger.debug(f"params: {params}") parsed_url = urlparse.urlparse(url) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index e8abe6fe4..2ca33554a 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -97,7 +97,6 @@ from litellm.proxy.utils import ( _is_projected_spend_over_limit, _get_projected_spend_over_limit, update_spend, - monitor_spend_list, ) from litellm.proxy.secret_managers.google_kms import load_google_kms from litellm.proxy.secret_managers.aws_secret_manager import load_aws_secret_manager @@ -118,6 +117,7 @@ from litellm.proxy.auth.auth_checks import ( allowed_routes_check, get_actual_routes, ) +from litellm.llms.custom_httpx.httpx_handler import HTTPHandler try: from litellm._version import version @@ -130,7 +130,6 @@ from fastapi import ( HTTPException, status, Depends, - BackgroundTasks, Header, Response, Form, @@ -305,6 +304,8 @@ proxy_logging_obj = ProxyLogging(user_api_key_cache=user_api_key_cache) async_result = None celery_app_conn = None celery_fn = None # Redis Queue for handling requests +### DB WRITER ### +db_writer_client: Optional[HTTPHandler] = None ### logger ### @@ -440,6 +441,8 @@ async def user_api_key_auth( request_body=request_data, team_object=team_object, end_user_object=end_user_object, + general_settings=general_settings, + route=route, ) # save user object in cache await user_api_key_cache.async_set_cache( @@ -867,6 +870,23 @@ async def user_api_key_auth( f"ExceededTokenBudget: Current Team Spend: {valid_token.team_spend}; Max Budget for Team: {valid_token.team_max_budget}" ) + # Check 8: Additional Common Checks across jwt + key auth + _team_obj = LiteLLM_TeamTable( + team_id=valid_token.team_id, + max_budget=valid_token.team_max_budget, + spend=valid_token.team_spend, + tpm_limit=valid_token.team_tpm_limit, + rpm_limit=valid_token.team_rpm_limit, + blocked=valid_token.team_blocked, + models=valid_token.team_models, + ) + _ = common_checks( + request_body=request_data, + team_object=_team_obj, + end_user_object=None, + general_settings=general_settings, + route=route, + ) # Token passed all checks api_key = valid_token.token @@ -1233,10 +1253,11 @@ async def update_database( user_ids.append(litellm_proxy_budget_name) ### KEY CHANGE ### for _id in user_ids: - prisma_client.user_list_transactons[_id] = ( - response_cost - + prisma_client.user_list_transactons.get(_id, 0) - ) + if _id is not None: + prisma_client.user_list_transactons[_id] = ( + response_cost + + prisma_client.user_list_transactons.get(_id, 0) + ) if end_user_id is not None: prisma_client.end_user_list_transactons[end_user_id] = ( response_cost @@ -1364,7 +1385,16 @@ async def update_database( ) payload["spend"] = response_cost - if prisma_client is not None: + if ( + os.getenv("SPEND_LOGS_URL", None) is not None + and prisma_client is not None + ): + if isinstance(payload["startTime"], datetime): + payload["startTime"] = payload["startTime"].isoformat() + if isinstance(payload["endTime"], datetime): + payload["endTime"] = payload["endTime"].isoformat() + prisma_client.spend_log_transactons.append(payload) + elif prisma_client is not None: await prisma_client.insert_data(data=payload, table_name="spend") except Exception as e: verbose_proxy_logger.debug( @@ -2615,11 +2645,7 @@ async def async_data_generator(response, user_api_key_dict): verbose_proxy_logger.debug( f"\033[1;31mAn error occurred: {e}\n\n Debug this by setting `--debug`, e.g. `litellm --model gpt-3.5-turbo --debug`" ) - router_model_names = ( - [m["model_name"] for m in llm_model_list] - if llm_model_list is not None - else [] - ) + router_model_names = llm_router.model_names if llm_router is not None else [] if user_debug: traceback.print_exc() @@ -2678,7 +2704,7 @@ def on_backoff(details): @router.on_event("startup") async def startup_event(): - global prisma_client, master_key, use_background_health_checks, llm_router, llm_model_list, general_settings, proxy_budget_rescheduler_min_time, proxy_budget_rescheduler_max_time, litellm_proxy_admin_name + global prisma_client, master_key, use_background_health_checks, llm_router, llm_model_list, general_settings, proxy_budget_rescheduler_min_time, proxy_budget_rescheduler_max_time, litellm_proxy_admin_name, db_writer_client import json ### LOAD MASTER KEY ### @@ -2711,6 +2737,8 @@ async def startup_event(): ## COST TRACKING ## cost_tracking() + db_writer_client = HTTPHandler() + proxy_logging_obj._init_litellm_callbacks() # INITIALIZE LITELLM CALLBACKS ON SERVER STARTUP <- do this to catch any logging errors on startup, not when calls are being made ## JWT AUTH ## @@ -2821,7 +2849,7 @@ async def startup_event(): update_spend, "interval", seconds=batch_writing_interval, - args=[prisma_client], + args=[prisma_client, db_writer_client], ) scheduler.start() @@ -2881,7 +2909,6 @@ async def completion( fastapi_response: Response, model: Optional[str] = None, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), - background_tasks: BackgroundTasks = BackgroundTasks(), ): global user_temperature, user_request_timeout, user_max_tokens, user_api_base try: @@ -2943,11 +2970,7 @@ async def completion( start_time = time.time() ### ROUTE THE REQUESTs ### - router_model_names = ( - [m["model_name"] for m in llm_model_list] - if llm_model_list is not None - else [] - ) + router_model_names = llm_router.model_names if llm_router is not None else [] # skip router if user passed their key if "api_key" in data: response = await litellm.atext_completion(**data) @@ -3047,7 +3070,6 @@ async def chat_completion( fastapi_response: Response, model: Optional[str] = None, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), - background_tasks: BackgroundTasks = BackgroundTasks(), ): global general_settings, user_debug, proxy_logging_obj, llm_model_list try: @@ -3161,11 +3183,8 @@ async def chat_completion( start_time = time.time() ### ROUTE THE REQUEST ### - router_model_names = ( - [m["model_name"] for m in llm_model_list] - if llm_model_list is not None - else [] - ) + # Do not change this - it should be a constant time fetch - ALWAYS + router_model_names = llm_router.model_names if llm_router is not None else [] # skip router if user passed their key if "api_key" in data: tasks.append(litellm.acompletion(**data)) @@ -3238,11 +3257,7 @@ async def chat_completion( verbose_proxy_logger.debug( f"\033[1;31mAn error occurred: {e}\n\n Debug this by setting `--debug`, e.g. `litellm --model gpt-3.5-turbo --debug`" ) - router_model_names = ( - [m["model_name"] for m in llm_model_list] - if llm_model_list is not None - else [] - ) + router_model_names = llm_router.model_names if llm_router is not None else [] if user_debug: traceback.print_exc() @@ -3284,7 +3299,6 @@ async def embeddings( request: Request, model: Optional[str] = None, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), - background_tasks: BackgroundTasks = BackgroundTasks(), ): global proxy_logging_obj try: @@ -3350,11 +3364,7 @@ async def embeddings( if data["model"] in litellm.model_alias_map: data["model"] = litellm.model_alias_map[data["model"]] - router_model_names = ( - [m["model_name"] for m in llm_model_list] - if llm_model_list is not None - else [] - ) + router_model_names = llm_router.model_names if llm_router is not None else [] if ( "input" in data and isinstance(data["input"], list) @@ -3460,7 +3470,6 @@ async def embeddings( async def image_generation( request: Request, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), - background_tasks: BackgroundTasks = BackgroundTasks(), ): global proxy_logging_obj try: @@ -3526,11 +3535,7 @@ async def image_generation( if data["model"] in litellm.model_alias_map: data["model"] = litellm.model_alias_map[data["model"]] - router_model_names = ( - [m["model_name"] for m in llm_model_list] - if llm_model_list is not None - else [] - ) + router_model_names = llm_router.model_names if llm_router is not None else [] ### CALL HOOKS ### - modify incoming data / reject request before calling the model data = await proxy_logging_obj.pre_call_hook( @@ -3674,11 +3679,7 @@ async def audio_transcriptions( **data, } # add the team-specific configs to the completion call - router_model_names = ( - [m["model_name"] for m in llm_model_list] - if llm_model_list is not None - else [] - ) + router_model_names = llm_router.model_names if llm_router is not None else [] assert ( file.filename is not None @@ -3843,11 +3844,7 @@ async def moderations( **data, } # add the team-specific configs to the completion call - router_model_names = ( - [m["model_name"] for m in llm_model_list] - if llm_model_list is not None - else [] - ) + router_model_names = llm_router.model_names if llm_router is not None else [] ### CALL HOOKS ### - modify incoming data / reject request before calling the model data = await proxy_logging_obj.pre_call_hook( @@ -4353,7 +4350,7 @@ async def info_key_fn( @router.get( "/spend/keys", - tags=["budget & spend Tracking"], + tags=["Budget & Spend Tracking"], dependencies=[Depends(user_api_key_auth)], ) async def spend_key_fn(): @@ -4385,7 +4382,7 @@ async def spend_key_fn(): @router.get( "/spend/users", - tags=["budget & spend Tracking"], + tags=["Budget & Spend Tracking"], dependencies=[Depends(user_api_key_auth)], ) async def spend_user_fn( @@ -4437,7 +4434,7 @@ async def spend_user_fn( @router.get( "/spend/tags", - tags=["budget & spend Tracking"], + tags=["Budget & Spend Tracking"], dependencies=[Depends(user_api_key_auth)], responses={ 200: {"model": List[LiteLLM_SpendLogs]}, @@ -4510,6 +4507,77 @@ async def view_spend_tags( ) +@router.post( + "/spend/calculate", + tags=["Budget & Spend Tracking"], + dependencies=[Depends(user_api_key_auth)], + responses={ + 200: { + "cost": { + "description": "The calculated cost", + "example": 0.0, + "type": "float", + } + } + }, +) +async def calculate_spend(request: Request): + """ + Accepts all the params of completion_cost. + + Calculate spend **before** making call: + + ``` + curl --location 'http://localhost:4000/spend/calculate' + --header 'Authorization: Bearer sk-1234' + --header 'Content-Type: application/json' + --data '{ + "model": "anthropic.claude-v2", + "messages": [{"role": "user", "content": "Hey, how'''s it going?"}] + }' + ``` + + Calculate spend **after** making call: + + ``` + curl --location 'http://localhost:4000/spend/calculate' + --header 'Authorization: Bearer sk-1234' + --header 'Content-Type: application/json' + --data '{ + "completion_response": { + "id": "chatcmpl-123", + "object": "chat.completion", + "created": 1677652288, + "model": "gpt-3.5-turbo-0125", + "system_fingerprint": "fp_44709d6fcb", + "choices": [{ + "index": 0, + "message": { + "role": "assistant", + "content": "Hello there, how may I assist you today?" + }, + "logprobs": null, + "finish_reason": "stop" + }] + "usage": { + "prompt_tokens": 9, + "completion_tokens": 12, + "total_tokens": 21 + } + } + }' + ``` + """ + from litellm import completion_cost + + data = await request.json() + if "completion_response" in data: + data["completion_response"] = litellm.ModelResponse( + **data["completion_response"] + ) + return {"cost": completion_cost(**data)} + + @router.get( "/spend/logs", tags=["Budget & Spend Tracking"], @@ -5240,6 +5308,7 @@ async def user_info( user_info = {"spend": spend} ## REMOVE HASHED TOKEN INFO before returning ## + returned_keys = [] for key in keys: try: key = key.model_dump() # noqa @@ -5248,10 +5317,24 @@ async def user_info( key = key.dict() key.pop("token", None) + if ( + "team_id" in key + and key["team_id"] is not None + and key["team_id"] != "litellm-dashboard" + ): + team_info = await prisma_client.get_data( + team_id=key["team_id"], table_name="team" + ) + team_alias = getattr(team_info, "team_alias", None) + key["team_alias"] = team_alias + else: + key["team_alias"] = "None" + returned_keys.append(key) + response_data = { "user_id": user_id, "user_info": user_info, - "keys": keys, + "keys": returned_keys, "teams": team_list, } return response_data @@ -5639,7 +5722,7 @@ async def new_team( raise HTTPException( status_code=400, detail={ - "error": f"tpm limit higher than user max. User tpm limit={user_api_key_dict.tpm_limit}" + "error": f"tpm limit higher than user max. User tpm limit={user_api_key_dict.tpm_limit}. User role={user_api_key_dict.user_role}" }, ) @@ -5651,7 +5734,7 @@ async def new_team( raise HTTPException( status_code=400, detail={ - "error": f"rpm limit higher than user max. User rpm limit={user_api_key_dict.rpm_limit}" + "error": f"rpm limit higher than user max. User rpm limit={user_api_key_dict.rpm_limit}. User role={user_api_key_dict.user_role}" }, ) @@ -5663,7 +5746,7 @@ async def new_team( raise HTTPException( status_code=400, detail={ - "error": f"max budget higher than user max. User max budget={user_api_key_dict.max_budget}" + "error": f"max budget higher than user max. User max budget={user_api_key_dict.max_budget}. User role={user_api_key_dict.user_role}" }, ) @@ -5673,7 +5756,7 @@ async def new_team( raise HTTPException( status_code=400, detail={ - "error": f"Model not in allowed user models. User allowed models={user_api_key_dict.models}" + "error": f"Model not in allowed user models. User allowed models={user_api_key_dict.models}. User id={user_api_key_dict.user_id}" }, ) @@ -6170,7 +6253,7 @@ async def block_team( raise Exception("No DB Connected.") record = await prisma_client.db.litellm_teamtable.update( - where={"team_id": data.team_id}, data={"blocked": True} + where={"team_id": data.team_id}, data={"blocked": True} # type: ignore ) return record @@ -6192,7 +6275,7 @@ async def unblock_team( raise Exception("No DB Connected.") record = await prisma_client.db.litellm_teamtable.update( - where={"team_id": data.team_id}, data={"blocked": False} + where={"team_id": data.team_id}, data={"blocked": False} # type: ignore ) return record @@ -6795,7 +6878,6 @@ async def async_queue_request( request: Request, model: Optional[str] = None, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), - background_tasks: BackgroundTasks = BackgroundTasks(), ): global general_settings, user_debug, proxy_logging_obj """ @@ -7058,6 +7140,13 @@ async def login(request: Request): except ImportError: subprocess.run(["pip", "install", "python-multipart"]) global master_key + if master_key is None: + raise ProxyException( + message="Master Key not set for Proxy. Please set Master Key to use Admin UI. Set `LITELLM_MASTER_KEY` in .env or set general_settings:master_key in config.yaml. https://docs.litellm.ai/docs/proxy/virtual_keys. If set, use `--detailed_debug` to debug issue.", + type="auth_error", + param="master_key", + code=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) form = await request.form() username = str(form.get("username")) password = str(form.get("password")) @@ -7997,6 +8086,8 @@ async def shutdown_event(): await jwt_handler.close() + if db_writer_client is not None: + await db_writer_client.close() ## RESET CUSTOM VARIABLES ## cleanup_router_config_variables() diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index 610ae50cd..5ce0670b1 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -5,7 +5,6 @@ datasource client { generator client { provider = "prisma-client-py" - previewFeatures = ["jsonProtocol"] } // Budget / Rate Limits for an org diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index ba8d70804..5923e0a5a 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -13,6 +13,7 @@ from litellm.proxy._types import ( Member, ) from litellm.caching import DualCache +from litellm.llms.custom_httpx.httpx_handler import HTTPHandler from litellm.proxy.hooks.parallel_request_limiter import ( _PROXY_MaxParallelRequestsHandler, ) @@ -298,6 +299,7 @@ class ProxyLogging: return else: user_info = str(user_info) + # percent of max_budget left to spend if user_max_budget > 0: percent_left = (user_max_budget - user_current_spend) / user_max_budget @@ -317,22 +319,35 @@ class ProxyLogging: ) return + ## PREVENTITIVE ALERTING ## - https://github.com/BerriAI/litellm/issues/2727 + # - Alert once within 28d period + # - Cache this information + # - Don't re-alert, if alert already sent + _cache: DualCache = self.call_details["user_api_key_cache"] + # check if 5% of max budget is left if percent_left <= 0.05: message = "5% budget left for" + user_info - await self.alerting_handler( - message=message, - level="Medium", - ) + result = await _cache.async_get_cache(key=message) + if result is None: + await self.alerting_handler( + message=message, + level="Medium", + ) + await _cache.async_set_cache(key=message, value="SENT", ttl=2419200) + return # check if 15% of max budget is left if percent_left <= 0.15: message = "15% budget left for" + user_info - await self.alerting_handler( - message=message, - level="Low", - ) + result = await _cache.async_get_cache(key=message) + if result is None: + await self.alerting_handler( + message=message, + level="Low", + ) + await _cache.async_set_cache(key=message, value="SENT", ttl=2419200) return return @@ -449,16 +464,15 @@ class ProxyLogging: Covers: 1. /chat/completions """ - new_response = copy.deepcopy(response) for callback in litellm.callbacks: try: if isinstance(callback, CustomLogger): await callback.async_post_call_success_hook( - user_api_key_dict=user_api_key_dict, response=new_response + user_api_key_dict=user_api_key_dict, response=response ) except Exception as e: raise e - return new_response + return response async def post_call_streaming_hook( self, @@ -1013,6 +1027,8 @@ class PrismaClient: t.max_budget AS team_max_budget, t.tpm_limit AS team_tpm_limit, t.rpm_limit AS team_rpm_limit, + t.models AS team_models, + t.blocked AS team_blocked, m.aliases as team_model_aliases FROM "LiteLLM_VerificationToken" AS v LEFT JOIN "LiteLLM_TeamTable" AS t ON v.team_id = t.team_id @@ -1023,6 +1039,10 @@ class PrismaClient: response = await self.db.query_first(query=sql_query) if response is not None: + if response["team_models"] is None: + response["team_models"] = [] + if response["team_blocked"] is None: + response["team_blocked"] = False response = LiteLLM_VerificationTokenView(**response) # for prisma we need to cast the expires time to str if response.expires is not None and isinstance( @@ -1867,7 +1887,7 @@ async def reset_budget(prisma_client: PrismaClient): async def update_spend( - prisma_client: PrismaClient, + prisma_client: PrismaClient, db_writer_client: Optional[HTTPHandler] ): """ Batch write updates to db. @@ -1995,13 +2015,30 @@ async def update_spend( except Exception as e: raise e + ### UPDATE SPEND LOGS ### + base_url = os.getenv("SPEND_LOGS_URL", None) + if ( + len(prisma_client.spend_log_transactons) > 0 + and base_url is not None + and db_writer_client is not None + ): + if not base_url.endswith("/"): + base_url += "/" + response = await db_writer_client.post( + url=base_url + "spend/update", + data=json.dumps(prisma_client.spend_log_transactons), # type: ignore + headers={"Content-Type": "application/json"}, + ) + if response.status_code == 200: + prisma_client.spend_log_transactons = [] -async def monitor_spend_list(prisma_client: PrismaClient): - """ - Check the length of each spend list, if it exceeds a threshold (e.g. 100 items) - write to db - """ - if len(prisma_client.user_list_transactons) > 10000: - await update_spend(prisma_client=prisma_client) + +# async def monitor_spend_list(prisma_client: PrismaClient): +# """ +# Check the length of each spend list, if it exceeds a threshold (e.g. 100 items) - write to db +# """ +# if len(prisma_client.user_list_transactons) > 10000: +# await update_spend(prisma_client=prisma_client) async def _read_request_body(request): diff --git a/litellm/router_strategy/lowest_tpm_rpm.py b/litellm/router_strategy/lowest_tpm_rpm.py index d2bf7bdb5..1e1e6df98 100644 --- a/litellm/router_strategy/lowest_tpm_rpm.py +++ b/litellm/router_strategy/lowest_tpm_rpm.py @@ -11,6 +11,7 @@ from litellm import token_counter from litellm.caching import DualCache from litellm.integrations.custom_logger import CustomLogger from litellm._logging import verbose_router_logger +from litellm.utils import print_verbose class LowestTPMLoggingHandler(CustomLogger): @@ -153,27 +154,21 @@ class LowestTPMLoggingHandler(CustomLogger): # Find lowest used model # ---------------------- lowest_tpm = float("inf") - deployment = None - if tpm_dict is None: # base case - none of the deployments have been used - # Return the 1st deployment where deployment["tpm"] >= input_tokens - for deployment in healthy_deployments: - _deployment_tpm = ( - deployment.get("tpm", None) - or deployment.get("litellm_params", {}).get("tpm", None) - or deployment.get("model_info", {}).get("tpm", None) - or float("inf") - ) - if _deployment_tpm >= input_tokens: - return deployment - return None + if tpm_dict is None: # base case - none of the deployments have been used + # initialize a tpm dict with {model_id: 0} + tpm_dict = {} + for deployment in healthy_deployments: + tpm_dict[deployment["model_info"]["id"]] = 0 + else: + for d in healthy_deployments: + ## if healthy deployment not yet used + if d["model_info"]["id"] not in tpm_dict: + tpm_dict[d["model_info"]["id"]] = 0 all_deployments = tpm_dict - for d in healthy_deployments: - ## if healthy deployment not yet used - if d["model_info"]["id"] not in all_deployments: - all_deployments[d["model_info"]["id"]] = 0 + deployment = None for item, item_tpm in all_deployments.items(): ## get the item from model list _deployment = None @@ -184,24 +179,27 @@ class LowestTPMLoggingHandler(CustomLogger): if _deployment is None: continue # skip to next one - _deployment_tpm = ( - _deployment.get("tpm", None) - or _deployment.get("litellm_params", {}).get("tpm", None) - or _deployment.get("model_info", {}).get("tpm", None) - or float("inf") - ) + _deployment_tpm = None + if _deployment_tpm is None: + _deployment_tpm = _deployment.get("tpm") + if _deployment_tpm is None: + _deployment_tpm = _deployment.get("litellm_params", {}).get("tpm") + if _deployment_tpm is None: + _deployment_tpm = _deployment.get("model_info", {}).get("tpm") + if _deployment_tpm is None: + _deployment_tpm = float("inf") - _deployment_rpm = ( - _deployment.get("rpm", None) - or _deployment.get("litellm_params", {}).get("rpm", None) - or _deployment.get("model_info", {}).get("rpm", None) - or float("inf") - ) + _deployment_rpm = None + if _deployment_rpm is None: + _deployment_rpm = _deployment.get("rpm") + if _deployment_rpm is None: + _deployment_rpm = _deployment.get("litellm_params", {}).get("rpm") + if _deployment_rpm is None: + _deployment_rpm = _deployment.get("model_info", {}).get("rpm") + if _deployment_rpm is None: + _deployment_rpm = float("inf") - if item_tpm == 0: - deployment = _deployment - break - elif item_tpm + input_tokens > _deployment_tpm: + if item_tpm + input_tokens > _deployment_tpm: continue elif (rpm_dict is not None and item in rpm_dict) and ( rpm_dict[item] + 1 > _deployment_rpm @@ -210,5 +208,5 @@ class LowestTPMLoggingHandler(CustomLogger): elif item_tpm < lowest_tpm: lowest_tpm = item_tpm deployment = _deployment - verbose_router_logger.info("returning picked lowest tpm/rpm deployment.") + print_verbose("returning picked lowest tpm/rpm deployment.") return deployment diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index 6d579acc0..e4d447908 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -1107,6 +1107,7 @@ def test_completion_openai_litellm_key(): # test_ completion_openai_litellm_key() +@pytest.mark.skip(reason="Unresponsive endpoint.[TODO] Rehost this somewhere else") def test_completion_ollama_hosted(): try: litellm.request_timeout = 20 # give ollama 20 seconds to response diff --git a/litellm/tests/test_image_generation.py b/litellm/tests/test_image_generation.py index 59ccaacd8..964b00582 100644 --- a/litellm/tests/test_image_generation.py +++ b/litellm/tests/test_image_generation.py @@ -31,6 +31,8 @@ def test_image_generation_openai(): except litellm.ContentPolicyViolationError: pass # OpenAI randomly raises these errors - skip when they occur except Exception as e: + if "Connection error" in str(e): + pass pytest.fail(f"An exception occurred - {str(e)}") @@ -53,6 +55,8 @@ def test_image_generation_azure(): except Exception as e: if "Your task failed as a result of our safety system." in str(e): pass + if "Connection error" in str(e): + pass else: pytest.fail(f"An exception occurred - {str(e)}") @@ -79,6 +83,8 @@ def test_image_generation_azure_dall_e_3(): except Exception as e: if "Your task failed as a result of our safety system." in str(e): pass + if "Connection error" in str(e): + pass else: pytest.fail(f"An exception occurred - {str(e)}") @@ -97,6 +103,8 @@ async def test_async_image_generation_openai(): except litellm.ContentPolicyViolationError: pass # openai randomly raises these errors - skip when they occur except Exception as e: + if "Connection error" in str(e): + pass pytest.fail(f"An exception occurred - {str(e)}") @@ -117,6 +125,8 @@ async def test_async_image_generation_azure(): except Exception as e: if "Your task failed as a result of our safety system." in str(e): pass + if "Connection error" in str(e): + pass else: pytest.fail(f"An exception occurred - {str(e)}") diff --git a/litellm/tests/test_router.py b/litellm/tests/test_router.py index 208fad983..16a3c2475 100644 --- a/litellm/tests/test_router.py +++ b/litellm/tests/test_router.py @@ -590,6 +590,8 @@ async def test_aimg_gen_on_router(): pass elif "Operation polling timed out" in str(e): pass + elif "Connection error" in str(e): + pass else: traceback.print_exc() pytest.fail(f"Error occurred: {e}") @@ -671,6 +673,8 @@ def test_aembedding_on_router(): pass elif "Operation polling timed out" in str(e): pass + elif "Connection error" in str(e): + pass else: traceback.print_exc() pytest.fail(f"Error occurred: {e}") diff --git a/litellm/tests/test_tpm_rpm_routing.py b/litellm/tests/test_tpm_rpm_routing.py index 588f3fab6..e7ada5eb8 100644 --- a/litellm/tests/test_tpm_rpm_routing.py +++ b/litellm/tests/test_tpm_rpm_routing.py @@ -264,7 +264,7 @@ def test_router_skip_rate_limited_deployments(): end_time=end_time, ) - ## CHECK WHAT'S SELECTED ## - should skip 2, and pick 1 + ## CHECK WHAT'S SELECTED ## # print(router.lowesttpm_logger.get_available_deployments(model_group="azure-model")) try: router.get_available_deployment( @@ -273,7 +273,41 @@ def test_router_skip_rate_limited_deployments(): ) pytest.fail(f"Should have raised No Models Available error") except Exception as e: - pass + print(f"An exception occurred! {str(e)}") + + +def test_single_deployment_tpm_zero(): + import litellm + import os + from datetime import datetime + + model_list = [ + { + "model_name": "gpt-3.5-turbo", + "litellm_params": { + "model": "gpt-3.5-turbo", + "api_key": os.getenv("OPENAI_API_KEY"), + "tpm": 0, + }, + } + ] + + router = litellm.Router( + model_list=model_list, + routing_strategy="usage-based-routing", + cache_responses=True, + ) + + model = "gpt-3.5-turbo" + messages = [{"content": "Hello, how are you?", "role": "user"}] + try: + router.get_available_deployment( + model=model, + messages=[{"role": "user", "content": "Hey, how's it going?"}], + ) + pytest.fail(f"Should have raised No Models Available error") + except Exception as e: + print(f"it worked - {str(e)}! \n{traceback.format_exc()}") @pytest.mark.asyncio diff --git a/litellm/tests/test_update_spend.py b/litellm/tests/test_update_spend.py index 0fd5d9bcf..4bc7f9fe4 100644 --- a/litellm/tests/test_update_spend.py +++ b/litellm/tests/test_update_spend.py @@ -92,4 +92,7 @@ async def test_batch_update_spend(prisma_client): setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") await litellm.proxy.proxy_server.prisma_client.connect() - await update_spend(prisma_client=litellm.proxy.proxy_server.prisma_client) + await update_spend( + prisma_client=litellm.proxy.proxy_server.prisma_client, + db_writer_client=None, + ) diff --git a/litellm/utils.py b/litellm/utils.py index d31bed6cb..fa15b73c2 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -2775,7 +2775,6 @@ def client(original_function): or isinstance(e, openai.Timeout) or isinstance(e, openai.APIConnectionError) ): - print_verbose(f"RETRY TRIGGERED!") kwargs["num_retries"] = num_retries return litellm.completion_with_retries(*args, **kwargs) elif ( @@ -3598,6 +3597,8 @@ def token_counter( raise ValueError("text and messages cannot both be None") elif isinstance(text, List): text = "".join(t for t in text if isinstance(t, str)) + elif isinstance(text, str): + count_response_tokens = True # user just trying to count tokens for a text. don't add the chat_ml +3 tokens to this if model is not None: tokenizer_json = _select_tokenizer(model=model) diff --git a/pyproject.toml b/pyproject.toml index 9940c43e4..b0907db1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.34.7" +version = "1.34.11" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -80,7 +80,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.34.7" +version = "1.34.11" version_files = [ "pyproject.toml:^version" ] diff --git a/requirements.txt b/requirements.txt index cd63c1242..9e6298363 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ fastapi>=0.109.1 # server dep pydantic>=2.5 # openai req. backoff==2.2.1 # server dep pyyaml>=6.0.1 # server dep -uvicorn==0.22.0 # server dep +uvicorn==0.29.0 # server dep gunicorn==21.2.0 # server dep boto3==1.34.34 # aws bedrock/sagemaker calls redis==5.0.0 # caching diff --git a/schema.prisma b/schema.prisma index 598848776..6e2400a12 100644 --- a/schema.prisma +++ b/schema.prisma @@ -5,7 +5,6 @@ datasource client { generator client { provider = "prisma-client-py" - previewFeatures = ["jsonProtocol"] } // Budget / Rate Limits for an org diff --git a/tests/test_openai_endpoints.py b/tests/test_openai_endpoints.py index 0d15c598b..432f2f1ab 100644 --- a/tests/test_openai_endpoints.py +++ b/tests/test_openai_endpoints.py @@ -198,6 +198,10 @@ async def image_generation(session, key): print() if status != 200: + if ( + "Connection error" in response_text + ): # OpenAI endpoint returns a connection error + return raise Exception(f"Request did not return a 200 status code: {status}") diff --git a/ui/litellm-dashboard/out/404.html b/ui/litellm-dashboard/out/404.html index 423a55d1f..81b64a1b0 100644 --- a/ui/litellm-dashboard/out/404.html +++ b/ui/litellm-dashboard/out/404.html @@ -1 +1 @@ -404: This page could not be found.🚅 LiteLLM

404

This page could not be found.

\ No newline at end of file +404: This page could not be found.🚅 LiteLLM

404

This page could not be found.

\ No newline at end of file diff --git a/ui/litellm-dashboard/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_buildManifest.js b/ui/litellm-dashboard/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_buildManifest.js new file mode 100644 index 000000000..f779caa02 --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_buildManifest.js @@ -0,0 +1 @@ +self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/_error":["static/chunks/pages/_error-d6107f1aac0c574c.js"],sortedPages:["/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/ui/litellm-dashboard/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_ssgManifest.js b/ui/litellm-dashboard/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_ssgManifest.js new file mode 100644 index 000000000..5b3ff592f --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/YrQPd5ySk25sMOIe_YoyJ/_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/ui/litellm-dashboard/out/_next/static/chunks/app/layout-24ae10436e315256.js b/ui/litellm-dashboard/out/_next/static/chunks/app/layout-24ae10436e315256.js new file mode 100644 index 000000000..e261adc05 --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/chunks/app/layout-24ae10436e315256.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[185],{87421:function(n,e,t){Promise.resolve().then(t.t.bind(t,99646,23)),Promise.resolve().then(t.t.bind(t,63385,23))},63385:function(){},99646:function(n){n.exports={style:{fontFamily:"'__Inter_c23dc8', '__Inter_Fallback_c23dc8'",fontStyle:"normal"},className:"__className_c23dc8"}}},function(n){n.O(0,[971,69,744],function(){return n(n.s=87421)}),_N_E=n.O()}]); \ No newline at end of file diff --git a/ui/litellm-dashboard/out/_next/static/chunks/app/page-8672e345e79b8043.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-8672e345e79b8043.js new file mode 100644 index 000000000..d196ba33f --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/chunks/app/page-8672e345e79b8043.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,s){Promise.resolve().then(s.bind(s,92182))},92182:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return eM}});var l=s(3827),r=s(64090),a=s(47907),n=s(8792),o=s(2179),i=e=>{let{userID:t,userRole:s,userEmail:r,showSSOBanner:a}=e;return console.log("User ID:",t),console.log("userEmail:",r),(0,l.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,l.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,l.jsx)("div",{className:"flex flex-col items-center",children:(0,l.jsx)(n.default,{href:"/",children:(0,l.jsx)("button",{className:"text-gray-800 text-2xl py-1 rounded text-center",children:(0,l.jsx)("img",{src:"/get_image",width:200,height:200,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,l.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[a?(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#setup-ssoauth-for-ui",target:"_blank",className:"mr-2"}):null,(0,l.jsxs)(o.Z,{variant:"secondary",size:"lg",children:[r,(0,l.jsxs)("p",{children:["Role: ",s]}),(0,l.jsxs)("p",{children:["ID: ",t]})]})]})]})},c=s(80588);let d=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await l.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/user/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await l.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t);let s=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},u=async function(e,t,s){let l=arguments.length>3&&void 0!==arguments[3]&&arguments[3];try{let r="/user/info";"App Owner"==s&&t&&(r="".concat(r,"?user_id=").concat(t)),console.log("in userInfoCall viewAll=",l),l&&(r="".concat(r,"?view_all=true"));let a=await fetch(r,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw c.ZP.error(e),Error("Network response was not ok")}let n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async e=>{try{let t=await fetch("/global/spend",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,t,s)=>{try{let t=await fetch("/v2/model/info",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t,s)=>{try{let t=await fetch("/model/metrics",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,t,s)=>{try{let t=await fetch("/models",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let s="/global/spend/logs";console.log("in keySpendLogsCall:",s);let l=await fetch("".concat(s,"?api_key=").concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let r=await l.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},w=async e=>{try{let t="/global/spend/teams";console.log("in teamSpendLogsCall:",t);let s=await fetch("".concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},Z=async(e,t,s,l,r,a)=>{try{console.log("user role in spend logs call: ".concat(s));let t="/spend/logs";t="App Owner"==s?"".concat(t,"?user_id=").concat(l,"&start_date=").concat(r,"&end_date=").concat(a):"".concat(t,"?start_date=").concat(r,"&end_date=").concat(a);let n=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let o=await n.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},f=async e=>{try{let t=await fetch("/global/spend/logs",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},k=async e=>{try{let t=await fetch("/global/spend/keys?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,t)=>{try{t&&JSON.stringify({api_key:t});let s={method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}};t&&(s.body=JSON.stringify({api_key:t}));let l=await fetch("/global/spend/end_users",s);if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let r=await l.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},b=async e=>{try{let t=await fetch("/global/spend/models?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},v=async(e,t)=>{try{let s=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},S=async(e,t,s,l)=>{try{let r=await fetch("/user/request_model",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({models:[t],user_id:s,justification:l})});if(!r.ok){let e=await r.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let a=await r.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},N=async e=>{try{let t="/user/get_requests";console.log("in userGetRequesedtModelsCall:",t);let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to get requested models:",e),e}},A=async(e,t)=>{try{let s="/user/get_users?role=".concat(t);console.log("in userGetAllUsersCall:",s);let l=await fetch(s,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let r=await l.json();return console.log(r),r}catch(e){throw console.error("Failed to get requested models:",e),e}},C=async(e,t)=>{try{console.log("Form Values in teamCreateCall:",t);let s=await fetch("/team/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let l=await s.json();return console.log("API Response:",l),l}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,t,s)=>{try{console.log("Form Values in teamMemberAddCall:",s);let l=await fetch("/team/member_add",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await l.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},I=async(e,t)=>{try{console.log("Form Values in userUpdateUserCall:",t);let s=await fetch("/user/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_role:"proxy_admin_viewer",...t})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let l=await s.json();return console.log("API Response:",l),l}catch(e){throw console.error("Failed to create key:",e),e}},E=async(e,t)=>{try{let s=await fetch("/global/predict/spend/logs",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({data:t})});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},P=async e=>{try{console.log("Checking Slack Budget Alerts service health");let t=await fetch("/health/services?service=slack_budget_alerts",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error("Failed Slack Alert test: "+e),Error(e)}let s=await t.json();return c.ZP.success("Test Slack Alert worked - check your Slack!"),console.log("Service Health Response:",s),s}catch(e){throw console.error("Failed to perform health check:",e),e}};var F=s(10384),O=s(46453),M=s(13810),R=s(71801),D=s(42440),U=s(17189),L=s(12143),B=s(77171),z=s(42539),K=s(88707),W=s(1861);let{Option:V}=U.default;var q=e=>{let{userID:t,team:s,userRole:a,accessToken:n,data:i,setData:h}=e,[m]=L.Z.useForm(),[u,x]=(0,r.useState)(!1),[p,j]=(0,r.useState)(null),[g,w]=(0,r.useState)(null),[Z,f]=(0,r.useState)([]),k=()=>{x(!1),m.resetFields()},_=()=>{x(!1),j(null),m.resetFields()};(0,r.useEffect)(()=>{(async()=>{try{if(null===t||null===a)return;if(null!==n){let e=(await y(n,t,a)).data.map(e=>e.id);console.log("available_model_names:",e),f(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,t,a]);let b=async e=>{try{c.ZP.info("Making API Call"),x(!0);let s=await d(n,t,e);console.log("key create Response:",s),h(e=>e?[...e,s]:[s]),j(s.key),w(s.soft_budget),c.ZP.success("API Key Created"),m.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}},v=async()=>{try{console.log("Sending Slack alert...");let e=await P(n);console.log("slackBudgetAlertsHealthCheck Response:",e),console.log("Testing Slack alert successful")}catch(e){console.error("Error sending Slack alert:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,l.jsx)(B.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:k,onCancel:_,children:(0,l.jsxs)(L.Z,{form:m,onFinish:b,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===a||"Admin"===a?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Team ID",name:"team_id",initialValue:s?s.team_id:null,valuePropName:"team_id",children:(0,l.jsx)(z.Z,{value:s?s.team_alias:"",disabled:!0})}),(0,l.jsx)(L.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(U.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:Z.map(e=>(0,l.jsx)(V,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Soft Budget (USD) Monthly",name:"soft_budget",initialValue:50,children:(0,l.jsx)(K.Z,{step:.01,precision:2,defaultValue:50,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(K.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Reset Budget",name:"budget_duration",children:(0,l.jsxs)(U.default,{defaultValue:null,placeholder:"n/a",children:[(0,l.jsx)(U.default.Option,{value:"24h",children:"daily"}),(0,l.jsx)(U.default.Option,{value:"30d",children:"monthly"})]})}),(0,l.jsx)(L.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(z.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,l.jsx)(z.Z,{placeholder:"default team (create a new team)"})}),(0,l.jsx)(L.Z.Item,{label:"Description",name:"description",children:(0,l.jsx)(z.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,l.jsx)(B.Z,{visible:u,onOk:k,onCancel:_,footer:null,children:(0,l.jsx)(O.Z,{numItems:1,className:"gap-2 w-full",children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Save your Key"}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,l.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,l.jsx)(F.Z,{numColSpan:1,children:null!=p?(0,l.jsxs)("div",{children:[(0,l.jsxs)(R.Z,{children:["API Key: ",p]}),(0,l.jsx)(D.Z,{className:"mt-6",children:"Budgets"}),(0,l.jsxs)(R.Z,{children:["Soft Limit Budget: $",g]}),(0,l.jsx)(o.Z,{className:"mt-3",onClick:v,children:"Test Slack Alert"}),(0,l.jsxs)(R.Z,{className:"mt-2",children:["(LiteLLM Docs -",(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/alerting",target:"_blank",className:"text-blue-500",children:"Set Up Slack Alerting)"})]})]}):(0,l.jsx)(R.Z,{children:"Key being created, this might take 30s"})})]})})})]})},J=s(33393),G=s(61244),$=s(10827),H=s(3851),Y=s(2044),X=s(64167),Q=s(74480),ee=s(7178),et=s(9853),es=s(56863),el=e=>{let{token:t,accessToken:s,keySpend:a,keyBudget:n,keyName:i}=e,[c,d]=(0,r.useState)(!1),[h,m]=(0,r.useState)(null),[u,x]=(0,r.useState)(""),[p,j]=(0,r.useState)(null),y=async()=>{try{if(null==s||null==t)return;console.log("accessToken: ".concat(s,"; token: ").concat(t));let e=await g(s,t);console.log("Response:",e),m(e);let l=await E(s,e);console.log("Response2:",l);let r=[...e,...l.response];m(r),x(l.predicted_spend),console.log("Combined Data:",r)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{size:"xs",onClick:()=>{console.log("Show Modal triggered"),d(!0),y()},variant:"secondary",children:"Spend Report"}),(0,l.jsxs)(B.Z,{visible:c,width:1400,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,l.jsxs)(D.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,l.jsxs)(es.Z,{children:["Monthly Spend $",a]}),(0,l.jsx)(D.Z,{children:u}),(0,l.jsx)(M.Z,{className:"mt-6 mb-6",children:h&&(0,l.jsx)(et.Z,{className:"mt-6",data:h,colors:["blue","amber"],index:"date",categories:["spend","predicted_spend"],yAxisWidth:80})})]})]}):null},er=e=>{let{userID:t,accessToken:s,data:a,setData:n}=e,[i,c]=(0,r.useState)(!1),[d,h]=(0,r.useState)(!1),[u,x]=(0,r.useState)(null),p=async e=>{null!=a&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=a){try{await m(s,u);let e=a.filter(e=>e.token!==u);n(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=a)return console.log("RERENDER TRIGGERED"),(0,l.jsxs)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,l.jsxs)($.Z,{className:"mt-5",children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Key Alias"}),(0,l.jsx)(Q.Z,{children:"Secret Key"}),(0,l.jsx)(Q.Z,{children:"Spend (USD)"}),(0,l.jsx)(Q.Z,{children:"Key Budget (USD)"}),(0,l.jsx)(Q.Z,{children:"Spend Report"}),(0,l.jsx)(Q.Z,{children:"Team ID"}),(0,l.jsx)(Q.Z,{children:"Metadata"}),(0,l.jsx)(Q.Z,{children:"Models"}),(0,l.jsx)(Q.Z,{children:"TPM / RPM Limits"}),(0,l.jsx)(Q.Z,{children:"Expires"})]})}),(0,l.jsx)(H.Z,{children:a.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,l.jsx)(R.Z,{children:e.key_alias}):(0,l.jsx)(R.Z,{children:"Not Set"})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:e.key_name})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(t){return e.spend}})()})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.max_budget?(0,l.jsx)(R.Z,{children:e.max_budget}):(0,l.jsx)(R.Z,{children:"Unlimited Budget"})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px"},children:(0,l.jsx)(el,{token:e.token,accessToken:s,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:e.team_alias&&"None"!=e.team_alias?e.team_alias:e.team_id})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:JSON.stringify(e.metadata).slice(0,400)})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:JSON.stringify(e.models)})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",overflowWrap:"break-word"},children:(0,l.jsxs)(R.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",wordWrap:"break-word"},children:null!=e.expires?(0,l.jsx)(R.Z,{children:e.expires}):(0,l.jsx)(R.Z,{children:"Never"})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"2px",wordWrap:"break-word"},children:(0,l.jsx)(G.Z,{onClick:()=>p(e.token),icon:J.Z,size:"sm"})})]},e.token))})]}),d&&(0,l.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,l.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,l.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,l.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,l.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,l.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,l.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,l.jsx)("div",{className:"sm:flex sm:items-start",children:(0,l.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,l.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,l.jsx)("div",{className:"mt-2",children:(0,l.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,l.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,l.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,l.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},ea=e=>{let{userID:t,userSpendData:s,userRole:a,accessToken:n}=e,[o,i]=(0,r.useState)(null==s?void 0:s.spend),[c,d]=(0,r.useState)((null==s?void 0:s.max_budget)||null);(0,r.useEffect)(()=>{(async()=>{if("Admin"===a)try{let e=await x(n);i(e.spend),d(e.max_budget||null)}catch(e){console.error("Error fetching global spend data:",e)}})()},[a,n]);let h=void 0!==o?o.toFixed(4):null;return(0,l.jsx)(l.Fragment,{children:(0,l.jsxs)(M.Z,{className:"mx-auto mb-4",children:[(0,l.jsxs)(es.Z,{children:["$",h]}),(0,l.jsxs)(D.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},en=s(36083),eo=s(68967),ei=s(27166),ec=e=>{let{teams:t,setSelectedTeam:s}=e,{Title:a,Paragraph:n}=en.default,[o,i]=(0,r.useState)("");return(0,l.jsxs)("div",{className:"mt-10",children:[(0,l.jsx)(a,{level:4,children:"Default Team"}),(0,l.jsx)(n,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),t&&t.length>0?(0,l.jsx)(eo.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(ei.Z,{value:String(t),onClick:()=>s(e),children:e.team_alias},t))}):(0,l.jsxs)(n,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]})},ed=s(37963);console.log("isLocal:",!1);var eh=e=>{let{userID:t,userRole:s,teams:n,keys:o,setUserRole:i,userEmail:c,setUserEmail:d,setTeams:h,setKeys:m}=e,[p,j]=(0,r.useState)(null),g=(0,a.useSearchParams)();g.get("viewSpend"),(0,a.useRouter)();let w=g.get("token"),[Z,f]=(0,r.useState)(null),[k,_]=(0,r.useState)([]),[b,v]=(0,r.useState)(n?n[0]:null);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,r.useEffect)(()=>{if(w){let e=(0,ed.o)(w);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),f(e.key),e.user_role){let t=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e)),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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),i(t)}else console.log("User role not defined");e.user_email?d(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&Z&&s&&!o&&!p){let e=sessionStorage.getItem("userModels"+t);e?_(JSON.parse(e)):(async()=>{try{let e=await u(Z,t,s);if(console.log("received teams in user dashboard: ".concat(Object.keys(e),"; team values: ").concat(Object.entries(e.teams))),"Admin"==s){let e=await x(Z);j(e),console.log("globalSpend:",e)}else j(e.user_info);m(e.keys),h(e.teams),v(e.teams?e.teams[0]:null),sessionStorage.setItem("userData"+t,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let l=(await y(Z,t,s)).data.map(e=>e.id);console.log("available_model_names:",l),_(l),console.log("userModels:",k),sessionStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,w,Z,o,s]),null==t||null==w){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}if(null==Z)return null;if(null==s&&i("App Owner"),s&&"Admin Viewer"==s){let{Title:e,Paragraph:t}=en.default;return(0,l.jsxs)("div",{children:[(0,l.jsx)(e,{level:1,children:"Access Denied"}),(0,l.jsx)(t,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",b),(0,l.jsx)("div",{children:(0,l.jsx)(O.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(ea,{userID:t,userSpendData:p,userRole:s,accessToken:Z}),(0,l.jsx)(er,{userID:t,accessToken:Z,data:o,setData:m}),(0,l.jsx)(q,{userID:t,team:b||null,userRole:s,accessToken:Z,data:o,setData:m},b?b.team_id:null),(0,l.jsx)(ec,{teams:n,setSelectedTeam:v})]})})})},em=s(5);let{Option:eu}=U.default;var ex=e=>{let{userModels:t,accessToken:s,userID:a}=e,[n]=L.Z.useForm(),[i,d]=(0,r.useState)(!1),h=async e=>{try{c.ZP.info("Requesting access");let{selectedModel:t,accessReason:l}=e;await S(s,t,a,l),d(!0)}catch(e){console.error("Error requesting access:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{size:"xs",onClick:()=>d(!0),children:"Request Access"}),(0,l.jsx)(B.Z,{title:"Request Access",visible:i,width:800,footer:null,onOk:()=>{d(!1),n.resetFields()},onCancel:()=>{d(!1),n.resetFields()},children:(0,l.jsxs)(L.Z,{form:n,onFinish:h,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(L.Z.Item,{label:"Select Model",name:"selectedModel",children:(0,l.jsx)(U.default,{placeholder:"Select model",style:{width:"100%"},children:t.map(e=>(0,l.jsx)(eu,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Reason for Access",name:"accessReason",children:(0,l.jsx)(z.Z.TextArea,{rows:4,placeholder:"Enter reason for access"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(o.Z,{children:"Request Access"})})]})})]})},ep=e=>{let{accessToken:t,token:s,userRole:a,userID:n}=e,[o,i]=(0,r.useState)({data:[]}),[c,d]=(0,r.useState)([]),[h,m]=(0,r.useState)([]);if((0,r.useEffect)(()=>{if(!t||!s||!a||!n)return;let e=async()=>{try{let e=await p(t,n,a);console.log("Model data response:",e.data),i(e);let s=await j(t,n,a);if(console.log("Model metrics response:",s),d(s),"Admin"===a&&t){let e=await N(t);console.log("Pending Requests:",h),m(e.requests||[])}}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&a&&n&&e()},[t,s,a,n]),!o||!t||!s||!a||!n)return(0,l.jsx)("div",{children:"Loading..."});let u=[];for(let e=0;e(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:(0,l.jsx)(D.Z,{children:e.model_name})}),(0,l.jsx)(Y.Z,{children:e.provider}),"Admin"===a&&(0,l.jsx)(Y.Z,{children:e.api_base}),(0,l.jsx)(Y.Z,{children:e.user_access?(0,l.jsx)(em.Z,{color:"green",children:"Yes"}):(0,l.jsx)(ex,{userModels:u,accessToken:t,userID:n})}),(0,l.jsx)(Y.Z,{children:e.input_cost}),(0,l.jsx)(Y.Z,{children:e.output_cost}),(0,l.jsx)(Y.Z,{children:e.max_tokens})]},e.model_name))})]})}),(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Model Statistics (Number Requests)"}),(0,l.jsx)(et.Z,{data:c,index:"model",categories:["num_requests"],colors:["blue"],yAxisWidth:400,layout:"vertical",tickGap:5})]}),(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Model Statistics (Latency)"}),(0,l.jsx)(et.Z,{data:c,index:"model",categories:["avg_latency_seconds"],colors:["red"],yAxisWidth:400,layout:"vertical",tickGap:5})]})]})})},ej=s(92836),ey=s(26734),eg=s(41608),ew=s(32126),eZ=s(23682);let{Option:ef}=U.default;var ek=e=>{let{userID:t,accessToken:s}=e,[a]=L.Z.useForm(),[n,i]=(0,r.useState)(!1),[d,m]=(0,r.useState)(null),[u,x]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{let e=await y(s,t,"any"),l=[];for(let t=0;t{i(!1),a.resetFields()},j=()=>{i(!1),m(null),a.resetFields()},g=async e=>{try{c.ZP.info("Making API Call"),i(!0),console.log("formValues in create user:",e);let l=await h(s,t,e);console.log("user create Response:",l),m(l.key),c.ZP.success("API user Created"),a.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the user:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>i(!0),children:"+ Create New User"}),(0,l.jsx)(B.Z,{title:"Create User",visible:n,width:800,footer:null,onOk:p,onCancel:j,children:(0,l.jsxs)(L.Z,{form:a,onFinish:g,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(L.Z.Item,{label:"User ID",name:"user_id",children:(0,l.jsx)(z.Z,{placeholder:"Enter User ID"})}),(0,l.jsx)(L.Z.Item,{label:"Team ID",name:"team_id",children:(0,l.jsx)(z.Z,{placeholder:"ai_team"})}),(0,l.jsx)(L.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(U.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:u.map(e=>(0,l.jsx)(ef,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(K.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(z.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Create User"})})]})}),d&&(0,l.jsxs)(B.Z,{title:"Save Your User",visible:n,onOk:p,onCancel:j,footer:null,children:[(0,l.jsxs)("p",{children:["Please save this secret user somewhere safe and accessible. For security reasons, ",(0,l.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret user, you will need to generate a new one."]}),(0,l.jsx)("p",{children:null!=d?"API user: ".concat(d):"User being created, this might take 30s"})]})]})},e_=e=>{let{accessToken:t,token:s,keys:a,userRole:n,userID:o,setKeys:i}=e,[c,d]=(0,r.useState)(null),[h,m]=(0,r.useState)(null),[x,p]=(0,r.useState)(1);if((0,r.useEffect)(()=>{if(!t||!s||!n||!o)return;let e=async()=>{try{let e=await u(t,null,n,!0);console.log("user data response:",e),d(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&n&&o&&!c&&e();let l=async()=>{try{let e=await _(t,null);console.log("user data response:",e),m(e)}catch(e){console.error("There was an error fetching the model data",e)}};n&&("Admin"==n||"Admin Viewer"==n)&&!h&&l()},[t,s,n,o]),!c||!t||!s||!n||!o)return(0,l.jsx)("div",{children:"Loading..."});let j=async e=>{try{let s=await _(t,e);console.log("user data response:",s),m(s)}catch(e){console.error("There was an error fetching the model data",e)}};return(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(O.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(ek,{userID:o,accessToken:t}),(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{variant:"line",defaultValue:"1",children:[(0,l.jsx)(ej.Z,{value:"1",children:"Key Owners"}),(0,l.jsx)(ej.Z,{value:"2",children:"End-Users"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsx)(ew.Z,{children:(0,l.jsxs)($.Z,{className:"mt-5",children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"User ID"}),(0,l.jsx)(Q.Z,{children:"User Role"}),(0,l.jsx)(Q.Z,{children:"User Models"}),(0,l.jsx)(Q.Z,{children:"User Spend ($ USD)"}),(0,l.jsx)(Q.Z,{children:"User Max Budget ($ USD)"})]})}),(0,l.jsx)(H.Z,{children:c.map(e=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.user_id}),(0,l.jsx)(Y.Z,{children:e.user_role?e.user_role:"app_owner"}),(0,l.jsx)(Y.Z,{children:e.models&&e.models.length>0?e.models:"All Models"}),(0,l.jsx)(Y.Z,{children:e.spend?e.spend:0}),(0,l.jsx)(Y.Z,{children:e.max_budget?e.max_budget:"Unlimited"})]},e.user_id))})]})}),(0,l.jsxs)(ew.Z,{children:[(0,l.jsxs)("div",{className:"flex items-center",children:[(0,l.jsx)("div",{className:"flex-1"}),(0,l.jsxs)("div",{className:"flex-1 flex justify-between items-center",children:[(0,l.jsx)(R.Z,{className:"w-1/4 mr-2 text-right",children:"Key"}),(0,l.jsx)(eo.Z,{defaultValue:"1",className:"w-3/4",children:null==a?void 0:a.map((e,t)=>{if(e&&null!==e.key_name&&e.key_name.length>0)return(0,l.jsx)(ei.Z,{value:String(t),onClick:()=>j(e.token),children:e.key_name},t)})})]})]}),(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"End User"}),(0,l.jsx)(Q.Z,{children:"Spend"}),(0,l.jsx)(Q.Z,{children:"Total Events"})]})}),(0,l.jsx)(H.Z,{children:null==h?void 0:h.map((e,t)=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.end_user}),(0,l.jsx)(Y.Z,{children:e.total_spend}),(0,l.jsx)(Y.Z,{children:e.total_events})]},t))})]})]})]})]})}),function(){if(!c)return null;let e=Math.ceil(c.length/25),t=Math.min(25*x,c.length);return(0,l.jsxs)("div",{className:"flex justify-between items-center",children:[(0,l.jsxs)("div",{children:["Showing ",(x-1)*25+1," – ",t," of ",c.length]}),(0,l.jsxs)("div",{className:"flex",children:[(0,l.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:1===x,onClick:()=>p(x-1),children:"← Prev"}),(0,l.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",disabled:x===e,onClick:()=>p(x+1),children:"Next →"})]})]})}()]})})},eb=e=>{let{teams:t,searchParams:s,accessToken:a,setTeams:n,userID:i,userRole:d}=e,[h]=L.Z.useForm(),[m]=L.Z.useForm(),{Title:u,Paragraph:x}=en.default,[p,j]=(0,r.useState)(""),[g,w]=(0,r.useState)(t?t[0]:null),[Z,f]=(0,r.useState)(!1),[k,_]=(0,r.useState)(!1),[b,v]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{if(null===i||null===d)return;if(null!==a){let e=(await y(a,i,d)).data.map(e=>e.id);console.log("available_model_names:",e),v(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,i,d]);let S=async e=>{try{if(null!=a){c.ZP.info("Creating Team");let s=await C(a,e);null!==t?n([...t,s]):n([s]),console.log("response for team create call: ".concat(s)),c.ZP.success("Team created"),f(!1)}}catch(e){console.error("Error creating the key:",e),c.ZP.error("Error creating the team: "+e)}},N=async e=>{try{if(null!=a&&null!=t){c.ZP.info("Adding Member");let s={role:"user",user_email:e.user_email,user_id:e.user_id},l=await T(a,g.team_id,s);console.log("response for team create call: ".concat(l.data));let r=t.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(l.data.team_id)),e.team_id===l.data.team_id));if(console.log("foundIndex: ".concat(r)),-1!==r){let e=[...t];e[r]=l.data,n(e),w(l.data)}_(!1)}}catch(e){console.error("Error creating the key:",e)}};return console.log("received teams ".concat(t)),(0,l.jsx)("div",{className:"w-full mx-4",children:(0,l.jsxs)(O.Z,{numItems:1,className:"gap-2 p-2 h-[75vh] w-full",children:[(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(u,{level:4,children:"All Teams"}),(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Team Name"}),(0,l.jsx)(Q.Z,{children:"Spend (USD)"}),(0,l.jsx)(Q.Z,{children:"Budget (USD)"}),(0,l.jsx)(Q.Z,{children:"Models"}),(0,l.jsx)(Q.Z,{children:"TPM / RPM Limits"})]})}),(0,l.jsx)(H.Z,{children:t&&t.length>0?t.map(e=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.max_budget?e.max_budget:"No limit"}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsx)(R.Z,{children:JSON.stringify(e.models?e.models:[])})}),(0,l.jsx)(Y.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,l.jsxs)(R.Z,{children:["TPM Limit:"," ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{}),"RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})})]},e.team_id)):null})]})})]}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>f(!0),children:"+ Create New Team"}),(0,l.jsx)(B.Z,{title:"Create Team",visible:Z,width:800,footer:null,onOk:()=>{f(!1),h.resetFields()},onCancel:()=>{f(!1),h.resetFields()},children:(0,l.jsxs)(L.Z,{form:h,onFinish:S,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Team Name",name:"team_alias",children:(0,l.jsx)(z.Z,{})}),(0,l.jsx)(L.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(U.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:b.map(e=>(0,l.jsx)(U.default.Option,{value:e,children:e},e))})}),(0,l.jsx)(L.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(K.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(L.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})}),(0,l.jsx)(L.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(K.Z,{step:1,width:400})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(u,{level:4,children:"Team Members"}),(0,l.jsx)(x,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),t&&t.length>0?(0,l.jsx)(eo.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(ei.Z,{value:String(t),onClick:()=>{w(e)},children:e.team_alias},t))}):(0,l.jsxs)(x,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Member Name"}),(0,l.jsx)(Q.Z,{children:"Role"})]})}),(0,l.jsx)(H.Z,{children:g?g.members_with_roles.map((e,t)=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,l.jsx)(Y.Z,{children:e.role})]},t)):null})]})})}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto mb-5",onClick:()=>_(!0),children:"+ Add member"}),(0,l.jsx)(B.Z,{title:"Add member",visible:k,width:800,footer:null,onOk:()=>{_(!1),m.resetFields()},onCancel:()=>{_(!1),m.resetFields()},children:(0,l.jsxs)(L.Z,{form:h,onFinish:N,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,l.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,l.jsx)(L.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},ev=s(8510),eS=e=>{let{searchParams:t,accessToken:s}=e,[a]=L.Z.useForm(),[n]=L.Z.useForm(),{Title:i,Paragraph:d}=en.default,[h,m]=(0,r.useState)(""),[u,x]=(0,r.useState)(null),[p,j]=(0,r.useState)(!1);(0,r.useEffect)(()=>{(async()=>{if(null!=s){let e=[],t=await A(s,"proxy_admin_viewer");t.forEach(t=>{e.push({user_role:t.user_role,user_id:t.user_id,user_email:t.user_email})}),console.log("proxy viewers: ".concat(t));let l=await A(s,"proxy_admin");l.forEach(t=>{e.push({user_role:t.user_role,user_id:t.user_id,user_email:t.user_email})}),console.log("proxy admins: ".concat(l)),console.log("combinedList: ".concat(e)),x(e)}})()},[s]);let y=async e=>{try{if(null!=s&&null!=u){c.ZP.info("Making API Call"),e.user_email,e.user_id;let t=await I(s,e);console.log("response for team create call: ".concat(t));let l=u.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(t.user_id)),e.user_id===t.user_id));console.log("foundIndex: ".concat(l)),-1==l&&(console.log("updates admin with new user"),u.push(t),x(u)),j(!1)}}catch(e){console.error("Error creating the key:",e)}};return console.log("admins: ".concat(null==u?void 0:u.length)),(0,l.jsxs)("div",{className:"w-full m-2",children:[(0,l.jsx)(i,{level:4,children:"Restricted Access"}),(0,l.jsxs)(d,{children:["Add other people to just view spend. They cannot create keys, teams or grant users access to new models."," ",(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"})]}),(0,l.jsxs)(O.Z,{numItems:1,className:"gap-2 p-0 w-full",children:[(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)($.Z,{children:[(0,l.jsx)(X.Z,{children:(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Q.Z,{children:"Member Name"}),(0,l.jsx)(Q.Z,{children:"Role"}),(0,l.jsx)(Q.Z,{children:"Action"})]})}),(0,l.jsx)(H.Z,{children:u?u.map((e,t)=>(0,l.jsxs)(ee.Z,{children:[(0,l.jsx)(Y.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,l.jsx)(Y.Z,{children:e.user_role}),(0,l.jsx)(Y.Z,{children:(0,l.jsx)(G.Z,{icon:ev.Z,size:"sm"})})]},t)):null})]})})}),(0,l.jsxs)(F.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto mb-5",onClick:()=>j(!0),children:"+ Add viewer"}),(0,l.jsx)(B.Z,{title:"Add viewer",visible:p,width:800,footer:null,onOk:()=>{j(!1),n.resetFields()},onCancel:()=>{j(!1),n.resetFields()},children:(0,l.jsxs)(L.Z,{form:a,onFinish:y,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(L.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,l.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,l.jsx)(L.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,l.jsx)(z.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(W.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})]})},eN=s(12968),eA=s(67951);async function eC(e,t,s,l){console.log("isLocal:",!1);let r=window.location.origin,a=new eN.ZP.OpenAI({apiKey:l,baseURL:r,dangerouslyAllowBrowser:!0});for await(let l of(await a.chat.completions.create({model:s,stream:!0,messages:[{role:"user",content:e}]})))console.log(l),l.choices[0].delta.content&&t(l.choices[0].delta.content)}var eT=e=>{let{accessToken:t,token:s,userRole:a,userID:n}=e,[o,i]=(0,r.useState)(""),[c,d]=(0,r.useState)([]),[h,m]=(0,r.useState)(void 0),[u,x]=(0,r.useState)(null);(0,r.useEffect)(()=>{t&&s&&a&&n&&(async()=>{let e=await y(t,n,a);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(x(e.data),m(e.data[0].id))})()},[t,n,a]);let p=(e,t)=>{d(s=>{let l=s[s.length-1];return l&&l.role===e?[...s.slice(0,s.length-1),{role:e,content:l.content+t}]:[...s,{role:e,content:t}]})},j=async()=>{if(""!==o.trim()&&t&&s&&a&&n){d(e=>[...e,{role:"user",content:o}]);try{h&&await eC(o,e=>p("assistant",e),h,t)}catch(e){console.error("Error fetching model response",e),p("assistant","Error fetching model response")}i("")}};if(a&&"Admin Viewer"==a){let{Title:e,Paragraph:t}=en.default;return(0,l.jsxs)("div",{children:[(0,l.jsx)(e,{level:1,children:"Access Denied"}),(0,l.jsx)(t,{children:"Ask your proxy admin for access to test models"})]})}return(0,l.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,l.jsx)(O.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,l.jsx)(M.Z,{children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{className:"mt-4",children:[(0,l.jsx)(ej.Z,{children:"Chat"}),(0,l.jsx)(ej.Z,{children:"API Reference"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsxs)(ew.Z,{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{children:"Select Model:"}),(0,l.jsx)("select",{value:h||"",onChange:e=>m(e.target.value),children:null==u?void 0:u.map(e=>(0,l.jsx)("option",{value:e.id,children:e.id},e.id))})]}),(0,l.jsxs)($.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,l.jsx)(X.Z,{children:(0,l.jsx)(ee.Z,{children:(0,l.jsx)(Y.Z,{children:(0,l.jsx)(D.Z,{children:"Chat"})})})}),(0,l.jsx)(H.Z,{children:c.map((e,t)=>(0,l.jsx)(ee.Z,{children:(0,l.jsx)(Y.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,l.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,l.jsxs)("div",{className:"flex",children:[(0,l.jsx)("input",{type:"text",value:o,onChange:e=>i(e.target.value),className:"flex-1 p-2 border rounded-md mr-2",placeholder:"Type your message..."}),(0,l.jsx)("button",{onClick:j,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,l.jsx)(ew.Z,{children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{children:[(0,l.jsx)(ej.Z,{children:"OpenAI Python SDK"}),(0,l.jsx)(ej.Z,{children:"LlamaIndex"}),(0,l.jsx)(ej.Z,{children:"Langchain Py"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsx)(ew.Z,{children:(0,l.jsx)(eA.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="http://0.0.0.0:4000" # proxy base url\n)\n\nresponse = client.chat.completions.create(\n model="gpt-3.5-turbo", # model to use from Models Tab\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ],\n extra_body={\n "metadata": {\n "generation_name": "ishaan-generation-openai-client",\n "generation_id": "openai-client-gen-id22",\n "trace_id": "openai-client-trace-id22",\n "trace_user_id": "openai-client-user-id2"\n }\n }\n)\n\nprint(response)\n '})}),(0,l.jsx)(ew.Z,{children:(0,l.jsx)(eA.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="azure-gpt-3.5", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="http://0.0.0.0:4000",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n '})}),(0,l.jsx)(ew.Z,{children:(0,l.jsx)(eA.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="http://0.0.0.0:8000",\n model = "gpt-3.5-turbo",\n temperature=0.1,\n extra_body={\n "metadata": {\n "generation_name": "ishaan-generation-langchain-client",\n "generation_id": "langchain-client-gen-id22",\n "trace_id": "langchain-client-trace-id22",\n "trace_user_id": "langchain-client-user-id2"\n }\n }\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n '})})]})]})})]})]})})})})},eI=s(33509),eE=s(30569);let{Sider:eP}=eI.default;var eF=e=>{let{setPage:t,userRole:s,defaultSelectedKey:r}=e;return"Admin Viewer"==s?(0,l.jsx)(eI.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,l.jsx)(eP,{width:120,children:(0,l.jsxs)(eE.Z,{mode:"inline",defaultSelectedKeys:r||["4"],style:{height:"100%",borderRight:0},children:[(0,l.jsx)(eE.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"4"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"1")]})})}):(0,l.jsx)(eI.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,l.jsx)(eP,{width:120,children:(0,l.jsxs)(eE.Z,{mode:"inline",defaultSelectedKeys:r||["1"],style:{height:"100%",borderRight:0},children:[(0,l.jsx)(eE.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,l.jsx)(eE.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4"),"Admin"==s?(0,l.jsx)(eE.Z.Item,{onClick:()=>t("users"),children:"Users"},"5"):null,"Admin"==s?(0,l.jsx)(eE.Z.Item,{onClick:()=>t("teams"),children:"Teams"},"6"):null,"Admin"==s?(0,l.jsx)(eE.Z.Item,{onClick:()=>t("admin-panel"),children:"Admin"},"7"):null]})})})},eO=e=>{let{accessToken:t,token:s,userRole:a,userID:n}=e,o=new Date,[i,c]=(0,r.useState)([]),[d,h]=(0,r.useState)([]),[m,u]=(0,r.useState)([]),[x,p]=(0,r.useState)([]),[j,y]=(0,r.useState)([]),[g,_]=(0,r.useState)([]),[S,N]=(0,r.useState)([]),A=new Date(o.getFullYear(),o.getMonth(),1),C=new Date(o.getFullYear(),o.getMonth()+1,0),T=E(A),I=E(C);function E(e){let t=e.getFullYear(),s=e.getMonth()+1,l=e.getDate();return"".concat(t,"-").concat(s<10?"0"+s:s,"-").concat(l<10?"0"+l:l)}return console.log("Start date is ".concat(T)),console.log("End date is ".concat(I)),(0,r.useEffect)(()=>{t&&s&&a&&n&&(async()=>{try{if(console.log("user role: ".concat(a)),"Admin"==a||"Admin Viewer"==a){let e=await f(t);c(e);let s=(await k(t)).map(e=>({key:(e.key_name||e.key_alias||e.api_key).substring(0,7),spend:e.total_spend}));h(s);let l=(await b(t)).map(e=>({key:e.model,spend:e.total_spend}));u(l);let r=await w(t);console.log("teamSpend",r),y(r.daily_spend),_(r.teams),N(r.total_spend_per_team)}else"App Owner"==a&&await Z(t,s,a,n,T,I).then(async e=>{if(console.log("result from spend logs call",e),"daily_spend"in e){let t=e.daily_spend;console.log("daily spend",t),c(t);let s=e.top_api_keys;h(s)}else{let s=(await v(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[s,l]=e;"spend"!==s&&"startTime"!==s&&"models"!==s&&"users"!==s&&t.push({key:s,spend:l})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let s=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(s[0]))),s}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));h(s),p(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[s,l]=e;""!==s&&null!=s&&"None"!=s&&(t[s]||(t[s]=0),t[s]+=l)})});let s=Object.entries(t).map(e=>{let[t,s]=e;return{user_id:t,spend:s}});s.sort((e,t)=>t.spend-e.spend);let l=s.slice(0,5);return console.log("topKeys: ".concat(Object.values(l[0]))),l}(e)),c(e)}})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,s,a,n,T,I]),(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(ey.Z,{children:[(0,l.jsxs)(eg.Z,{className:"mt-4",children:[(0,l.jsx)(ej.Z,{children:"All Up"}),(0,l.jsx)(ej.Z,{children:"Team Based Usage"})]}),(0,l.jsxs)(eZ.Z,{children:[(0,l.jsx)(ew.Z,{children:(0,l.jsxs)(O.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(F.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Monthly Spend"}),(0,l.jsx)(et.Z,{data:i,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Top API Keys"}),(0,l.jsx)(et.Z,{className:"mt-4 h-40",data:d,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Top Users"}),(0,l.jsx)(et.Z,{className:"mt-4 h-40",data:x,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,l.jsx)(F.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Top Models"}),(0,l.jsx)(et.Z,{className:"mt-4 h-40",data:m,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})}),(0,l.jsx)(ew.Z,{children:(0,l.jsxs)(O.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(F.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Daily Spend Per Team"}),(0,l.jsx)(et.Z,{className:"h-72",data:j,index:"date",categories:g,yAxisWidth:30,stack:!0})]})}),(0,l.jsx)(F.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(D.Z,{children:"Total Spend Per Team"}),(0,l.jsx)(et.Z,{className:"h-72",data:S,index:"team_id",categories:["total_spend"],yAxisWidth:30})]})})]})})]})]})})},eM=()=>{let{Title:e,Paragraph:t}=en.default,[s,n]=(0,r.useState)(""),[o,c]=(0,r.useState)(null),[d,h]=(0,r.useState)(null),[m,u]=(0,r.useState)(null),[x,p]=(0,r.useState)(!0),j=(0,a.useSearchParams)(),y=j.get("userID"),g=j.get("token"),[w,Z]=(0,r.useState)("api-keys"),[f,k]=(0,r.useState)(null);return(0,r.useEffect)(()=>{if(g){let e=(0,ed.o)(g);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),k(e.key),e.user_role){let t=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e.toLowerCase())),console.log("Received user role length: ".concat(e.toLowerCase().length)),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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),n(t),"Admin Viewer"==t&&Z("usage")}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?p("username_password"==e.login_method):console.log("User Email is not set ".concat(e))}}},[g]),(0,l.jsx)(r.Suspense,{fallback:(0,l.jsx)("div",{children:"Loading..."}),children:(0,l.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,l.jsx)(i,{userID:y,userRole:s,userEmail:o,showSSOBanner:x}),(0,l.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,l.jsx)(eF,{setPage:Z,userRole:s,defaultSelectedKey:null}),"api-keys"==w?(0,l.jsx)(eh,{userID:y,userRole:s,teams:d,keys:m,setUserRole:n,userEmail:o,setUserEmail:c,setTeams:h,setKeys:u}):"models"==w?(0,l.jsx)(ep,{userID:y,userRole:s,token:g,accessToken:f}):"llm-playground"==w?(0,l.jsx)(eT,{userID:y,userRole:s,token:g,accessToken:f}):"users"==w?(0,l.jsx)(e_,{userID:y,userRole:s,token:g,keys:m,accessToken:f,setKeys:u}):"teams"==w?(0,l.jsx)(eb,{teams:d,setTeams:h,searchParams:j,accessToken:f,userID:y,userRole:s}):"admin-panel"==w?(0,l.jsx)(eS,{setTeams:h,searchParams:j,accessToken:f}):(0,l.jsx)(eO,{userID:y,userRole:s,token:g,accessToken:f})]})]})})}}},function(e){e.O(0,[730,971,69,744],function(){return e(e.s=20661)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/ui/litellm-dashboard/out/_next/static/chunks/main-app-9b4fb13a7db53edf.js b/ui/litellm-dashboard/out/_next/static/chunks/main-app-9b4fb13a7db53edf.js new file mode 100644 index 000000000..440df3cb3 --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/chunks/main-app-9b4fb13a7db53edf.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[744],{32028:function(e,n,t){Promise.resolve().then(t.t.bind(t,47690,23)),Promise.resolve().then(t.t.bind(t,48955,23)),Promise.resolve().then(t.t.bind(t,5613,23)),Promise.resolve().then(t.t.bind(t,11902,23)),Promise.resolve().then(t.t.bind(t,31778,23)),Promise.resolve().then(t.t.bind(t,77831,23))}},function(e){var n=function(n){return e(e.s=n)};e.O(0,[971,69],function(){return n(35317),n(32028)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/ui/litellm-dashboard/out/index.html b/ui/litellm-dashboard/out/index.html index b138e2de3..09eaf59a0 100644 --- a/ui/litellm-dashboard/out/index.html +++ b/ui/litellm-dashboard/out/index.html @@ -1 +1 @@ -🚅 LiteLLM \ No newline at end of file +🚅 LiteLLM \ No newline at end of file diff --git a/ui/litellm-dashboard/out/index.txt b/ui/litellm-dashboard/out/index.txt index d70e09bac..1eac71362 100644 --- a/ui/litellm-dashboard/out/index.txt +++ b/ui/litellm-dashboard/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[19914,["730","static/chunks/730-1411b729a1c79695.js","931","static/chunks/app/page-144687b251040a22.js"],""] +3:I[92182,["730","static/chunks/730-1411b729a1c79695.js","931","static/chunks/app/page-8672e345e79b8043.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["aIO8mtlEIEUTmgL8cGjve",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_c23dc8","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",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."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/68a21c6e6697f7ca.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["YrQPd5ySk25sMOIe_YoyJ",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_c23dc8","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",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."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/68a21c6e6697f7ca.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] 6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"🚅 LiteLLM"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]] 1:null diff --git a/ui/litellm-dashboard/src/components/create_key_button.tsx b/ui/litellm-dashboard/src/components/create_key_button.tsx index fa80556b5..82ce56ed8 100644 --- a/ui/litellm-dashboard/src/components/create_key_button.tsx +++ b/ui/litellm-dashboard/src/components/create_key_button.tsx @@ -12,33 +12,32 @@ import { Select, message, } from "antd"; -import { keyCreateCall, slackBudgetAlertsHealthCheck } from "./networking"; +import { keyCreateCall, slackBudgetAlertsHealthCheck, modelAvailableCall } from "./networking"; const { Option } = Select; interface CreateKeyProps { userID: string; - teamID: string | null; + team: any | null; userRole: string | null; accessToken: string; data: any[] | null; - userModels: string[]; setData: React.Dispatch>; } const CreateKey: React.FC = ({ userID, - teamID, + team, userRole, accessToken, data, - userModels, setData, }) => { const [form] = Form.useForm(); const [isModalVisible, setIsModalVisible] = useState(false); const [apiKey, setApiKey] = useState(null); const [softBudget, setSoftBudget] = useState(null); + const [userModels, setUserModels] = useState([]); const handleOk = () => { setIsModalVisible(false); form.resetFields(); @@ -50,6 +49,29 @@ const CreateKey: React.FC = ({ form.resetFields(); }; + useEffect(() => { + const fetchUserModels = async () => { + try { + if (userID === null || userRole === null) { + return; + } + + if (accessToken !== null) { + const model_available = await modelAvailableCall(accessToken, userID, userRole); + let available_model_names = model_available["data"].map( + (element: { id: string }) => element.id + ); + console.log("available_model_names:", available_model_names); + setUserModels(available_model_names); + } + } catch (error) { + console.error("Error fetching user models:", error); + } + }; + + fetchUserModels(); + }, [accessToken, userID, userRole]); + const handleCreate = async (formValues: Record) => { try { message.info("Making API Call"); @@ -105,12 +127,15 @@ const CreateKey: React.FC = ({ - - + + + - + diff --git a/ui/litellm-dashboard/src/components/navbar.tsx b/ui/litellm-dashboard/src/components/navbar.tsx index 753497e3b..ae27d51d8 100644 --- a/ui/litellm-dashboard/src/components/navbar.tsx +++ b/ui/litellm-dashboard/src/components/navbar.tsx @@ -59,9 +59,6 @@ const Navbar: React.FC = ({ target="_blank" className="mr-2" > - ) : null} diff --git a/ui/litellm-dashboard/src/components/teams.tsx b/ui/litellm-dashboard/src/components/teams.tsx index d1c02d17d..f1d5d6555 100644 --- a/ui/litellm-dashboard/src/components/teams.tsx +++ b/ui/litellm-dashboard/src/components/teams.tsx @@ -102,7 +102,7 @@ const Team: React.FC = ({ const handleCreate = async (formValues: Record) => { try { if (accessToken != null) { - //message.info("Making API Call"); + message.info("Creating Team"); const response: any = await teamCreateCall(accessToken, formValues); if (teams !== null) { setTeams([...teams, response]); @@ -122,7 +122,7 @@ const Team: React.FC = ({ const handleMemberCreate = async (formValues: Record) => { try { if (accessToken != null && teams != null) { - message.info("Making API Call"); + message.info("Adding Member"); const user_role: Member = { role: "user", user_email: formValues.user_email, @@ -157,7 +157,7 @@ const Team: React.FC = ({ }; console.log(`received teams ${teams}`); return ( -
+
All Teams @@ -168,6 +168,7 @@ const Team: React.FC = ({ Team Name Spend (USD) Budget (USD) + Models TPM / RPM Limits @@ -176,22 +177,25 @@ const Team: React.FC = ({ {teams && teams.length > 0 ? teams.map((team: any) => ( - {team["team_alias"]} - {team["spend"]} - + {team["team_alias"]} + {team["spend"]} + {team["max_budget"] ? team["max_budget"] : "No limit"} - + + {JSON.stringify(team["models"] ? team["models"] : [])} + + TPM Limit:{" "} {team.tpm_limit ? team.tpm_limit : "Unlimited"}{" "} -

RPM Limit:{" "} +

RPM Limit:{" "} {team.rpm_limit ? team.rpm_limit : "Unlimited"}
- + {/* - + */}
)) : null} @@ -293,7 +297,7 @@ const Team: React.FC = ({ Member Name Role - Action + {/* Action */} @@ -310,9 +314,9 @@ const Team: React.FC = ({ : null} {member["role"]} - + {/* - + */} ) ) diff --git a/ui/litellm-dashboard/src/components/user_dashboard.tsx b/ui/litellm-dashboard/src/components/user_dashboard.tsx index d164a35a1..9180fa3cd 100644 --- a/ui/litellm-dashboard/src/components/user_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/user_dashboard.tsx @@ -203,6 +203,8 @@ const UserDashboard: React.FC = ({ ); } + console.log("inside user dashboard, selected team", selectedTeam); + return (
@@ -220,10 +222,10 @@ const UserDashboard: React.FC = ({ setData={setKeys} /> = ({ /> - {item.team_id} + {item.team_alias && item.team_alias != "None" ? item.team_alias : item.team_id} {JSON.stringify(item.metadata).slice(0, 400)}