add knip as a dev dependency, remove some unused files
This commit is contained in:
parent
5de7fe2897
commit
54828e3783
18
ui/litellm-dashboard/knip.json
Normal file
18
ui/litellm-dashboard/knip.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/knip@5/schema.json",
|
||||
"entry": ["scripts/**/*.ts"],
|
||||
"project": [
|
||||
"src/**/*.{ts,tsx}",
|
||||
"tests/**/*.{ts,tsx}",
|
||||
"scripts/**/*.ts",
|
||||
"e2e_tests/**/*.ts"
|
||||
],
|
||||
"playwright": {
|
||||
"config": "e2e_tests/playwright.config.ts",
|
||||
"entry": [
|
||||
"e2e_tests/**/*.spec.ts",
|
||||
"e2e_tests/**/*.setup.ts",
|
||||
"e2e_tests/globalSetup.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
517
ui/litellm-dashboard/package-lock.json
generated
517
ui/litellm-dashboard/package-lock.json
generated
@ -59,10 +59,11 @@
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-unused-imports": "^4.2.0",
|
||||
"jsdom": "^27.0.0",
|
||||
"knip": "^5.83.1",
|
||||
"postcss": "^8.4.33",
|
||||
"prettier": "3.2.5",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "5.3.3",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^7.1.11",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
@ -2044,6 +2045,306 @@
|
||||
"node": ">=12.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-android-arm-eabi": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.17.0.tgz",
|
||||
"integrity": "sha512-kVnY21v0GyZ/+LG6EIO48wK3mE79BUuakHUYLIqobO/Qqq4mJsjuYXMSn3JtLcKZpN1HDVit4UHpGJHef1lrlw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-android-arm64": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.17.0.tgz",
|
||||
"integrity": "sha512-Pf8e3XcsK9a8RHInoAtEcrwf2vp7V9bSturyUUYxw9syW6E7cGi7z9+6ADXxm+8KAevVfLA7pfBg8NXTvz/HOw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-darwin-arm64": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.17.0.tgz",
|
||||
"integrity": "sha512-lVSgKt3biecofXVr8e1hnfX0IYMd4A6VCxmvOmHsFt5Zbmt0lkO4S2ap2bvQwYDYh5ghUNamC7M2L8K6vishhQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-darwin-x64": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.17.0.tgz",
|
||||
"integrity": "sha512-+/raxVJE1bo7R4fA9Yp0wm3slaCOofTEeUzM01YqEGcRDLHB92WRGjRhagMG2wGlvqFuSiTp81DwSbBVo/g6AQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-freebsd-x64": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.17.0.tgz",
|
||||
"integrity": "sha512-x9Ks56n+n8h0TLhzA6sJXa2tGh3uvMGpBppg6PWf8oF0s5S/3p/J6k1vJJ9lIUtTmenfCQEGKnFokpRP4fLTLg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.17.0.tgz",
|
||||
"integrity": "sha512-Wf3w07Ow9kXVJrS0zmsaFHKOGhXKXE8j1tNyy+qIYDsQWQ4UQZVx5SjlDTcqBnFerlp3Z3Is0RjmVzgoLG3qkA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-arm-musleabihf": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.17.0.tgz",
|
||||
"integrity": "sha512-N0OKA1al1gQ5Gm7Fui1RWlXaHRNZlwMoBLn3TVtSXX+WbnlZoVyDqqOqFL8+pVEHhhxEA2LR8kmM0JO6FAk6dg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-arm64-gnu": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.17.0.tgz",
|
||||
"integrity": "sha512-wdcQ7Niad9JpjZIGEeqKJnTvczVunqlZ/C06QzR5zOQNeLVRScQ9S5IesKWUAPsJQDizV+teQX53nTK+Z5Iy+g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-arm64-musl": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.17.0.tgz",
|
||||
"integrity": "sha512-65B2/t39HQN5AEhkLsC+9yBD1iRUkKOIhfmJEJ7g6wQ9kylra7JRmNmALFjbsj0VJsoSQkpM8K07kUZuNJ9Kxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-ppc64-gnu": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.17.0.tgz",
|
||||
"integrity": "sha512-kExgm3TLK21dNMmcH+xiYGbc6BUWvT03PUZ2aYn8mUzGPeeORklBhg3iYcaBI3ZQHB25412X1Z6LLYNjt4aIaA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-riscv64-gnu": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.17.0.tgz",
|
||||
"integrity": "sha512-1utUJC714/ydykZQE8c7QhpEyM4SaslMfRXxN9G61KYazr6ndt85LaubK3EZCSD50vVEfF4PVwFysCSO7LN9uA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-riscv64-musl": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.17.0.tgz",
|
||||
"integrity": "sha512-mayiYOl3LMmtO2CLn4I5lhanfxEo0LAqlT/EQyFbu1ZN3RS+Xa7Q3JEM0wBpVIyfO/pqFrjvC5LXw/mHNDEL7A==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-s390x-gnu": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.17.0.tgz",
|
||||
"integrity": "sha512-Ow/yI+CrUHxIIhn/Y1sP/xoRKbCC3x9O1giKr3G/pjMe+TCJ5ZmfqVWU61JWwh1naC8X5Xa7uyLnbzyYqPsHfg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-x64-gnu": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.17.0.tgz",
|
||||
"integrity": "sha512-Z4J7XlPMQOLPANyu6y3B3V417Md4LKH5bV6bhqgaG99qLHmU5LV2k9ErV14fSqoRc/GU/qOpqMdotxiJqN/YWg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-linux-x64-musl": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.17.0.tgz",
|
||||
"integrity": "sha512-0effK+8lhzXsgsh0Ny2ngdnTPF30v6QQzVFApJ1Ctk315YgpGkghkelvrLYYgtgeFJFrzwmOJ2nDvCrUFKsS2Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-openharmony-arm64": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.17.0.tgz",
|
||||
"integrity": "sha512-kFB48dRUW6RovAICZaxHKdtZe+e94fSTNA2OedXokzMctoU54NPZcv0vUX5PMqyikLIKJBIlW7laQidnAzNrDA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-wasm32-wasi": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.17.0.tgz",
|
||||
"integrity": "sha512-a3elKSBLPT0OoRPxTkCIIc+4xnOELolEBkPyvdj01a6PSdSmyJ1NExWjWLaXnT6wBMblvKde5RmSwEi3j+jZpg==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@napi-rs/wasm-runtime": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
||||
"integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.7.1",
|
||||
"@emnapi/runtime": "^1.7.1",
|
||||
"@tybys/wasm-util": "^0.10.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-win32-arm64-msvc": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.17.0.tgz",
|
||||
"integrity": "sha512-4eszUsSDb9YVx0RtYkPWkxxtSZIOgfeiX//nG5cwRRArg178w4RCqEF1kbKPud9HPrp1rXh7gE4x911OhvTnPg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-win32-ia32-msvc": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.17.0.tgz",
|
||||
"integrity": "sha512-t946xTXMmR7yGH0KAe9rB055/X4EPIu93JUvjchl2cizR5QbuwkUV7vLS2BS6x6sfvDoQb6rWYnV1HCci6tBSg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@oxc-resolver/binding-win32-x64-msvc": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.17.0.tgz",
|
||||
"integrity": "sha512-pX6s2kMXLQg+hlqKk5UqOW09iLLxnTkvn8ohpYp2Mhsm2yzDPCx9dyOHiB/CQixLzTkLQgWWJykN4Z3UfRKW4Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.1.tgz",
|
||||
@ -6266,6 +6567,16 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/fd-package-json": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz",
|
||||
"integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"walk-up-path": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
@ -6422,6 +6733,22 @@
|
||||
"node": ">=0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/formatly": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz",
|
||||
"integrity": "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fd-package-json": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"formatly": "bin/index.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-node": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
|
||||
@ -7747,6 +8074,114 @@
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/knip": {
|
||||
"version": "5.83.1",
|
||||
"resolved": "https://registry.npmjs.org/knip/-/knip-5.83.1.tgz",
|
||||
"integrity": "sha512-av3ZG/Nui6S/BNL8Tmj12yGxYfTnwWnslouW97m40him7o8MwiMjZBY9TPvlEWUci45aVId0/HbgTwSKIDGpMw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/webpro"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/knip"
|
||||
}
|
||||
],
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"fast-glob": "^3.3.3",
|
||||
"formatly": "^0.3.0",
|
||||
"jiti": "^2.6.0",
|
||||
"js-yaml": "^4.1.1",
|
||||
"minimist": "^1.2.8",
|
||||
"oxc-resolver": "^11.15.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"picomatch": "^4.0.1",
|
||||
"smol-toml": "^1.5.2",
|
||||
"strip-json-comments": "5.0.3",
|
||||
"zod": "^4.1.11"
|
||||
},
|
||||
"bin": {
|
||||
"knip": "bin/knip.js",
|
||||
"knip-bun": "bin/knip-bun.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18",
|
||||
"typescript": ">=5.0.4 <7"
|
||||
}
|
||||
},
|
||||
"node_modules/knip/node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"glob-parent": "^5.1.2",
|
||||
"merge2": "^1.3.0",
|
||||
"micromatch": "^4.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/knip/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/knip/node_modules/jiti": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
||||
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/knip/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/knip/node_modules/strip-json-comments": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
|
||||
"integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/language-subtag-registry": {
|
||||
"version": "0.3.23",
|
||||
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
|
||||
@ -9109,6 +9544,38 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/oxc-resolver": {
|
||||
"version": "11.17.0",
|
||||
"resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.17.0.tgz",
|
||||
"integrity": "sha512-R5P2Tw6th+nQJdNcZGfuppBS/sM0x1EukqYffmlfX2xXLgLGCCPwu4ruEr9Sx29mrpkHgITc130Qps2JR90NdQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/Boshen"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@oxc-resolver/binding-android-arm-eabi": "11.17.0",
|
||||
"@oxc-resolver/binding-android-arm64": "11.17.0",
|
||||
"@oxc-resolver/binding-darwin-arm64": "11.17.0",
|
||||
"@oxc-resolver/binding-darwin-x64": "11.17.0",
|
||||
"@oxc-resolver/binding-freebsd-x64": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-arm-musleabihf": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-arm64-gnu": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-arm64-musl": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-ppc64-gnu": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-riscv64-musl": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-s390x-gnu": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-x64-gnu": "11.17.0",
|
||||
"@oxc-resolver/binding-linux-x64-musl": "11.17.0",
|
||||
"@oxc-resolver/binding-openharmony-arm64": "11.17.0",
|
||||
"@oxc-resolver/binding-wasm32-wasi": "11.17.0",
|
||||
"@oxc-resolver/binding-win32-arm64-msvc": "11.17.0",
|
||||
"@oxc-resolver/binding-win32-ia32-msvc": "11.17.0",
|
||||
"@oxc-resolver/binding-win32-x64-msvc": "11.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||
@ -11109,6 +11576,19 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/smol-toml": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz",
|
||||
"integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/cyyynthia"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@ -12414,6 +12894,16 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/walk-up-path": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz",
|
||||
"integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "4.0.0-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
|
||||
@ -12650,6 +13140,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zwitch": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
|
||||
@ -12659,21 +13159,6 @@
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "14.2.33",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz",
|
||||
"integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,9 @@
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"e2e": "playwright test --config e2e_tests/playwright.config.ts",
|
||||
"e2e:ui": "playwright test --ui --config e2e_tests/playwright.config.ts"
|
||||
"e2e:ui": "playwright test --ui --config e2e_tests/playwright.config.ts",
|
||||
"knip": "knip",
|
||||
"knip:fix": "knip --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.54.0",
|
||||
@ -68,10 +70,11 @@
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-unused-imports": "^4.2.0",
|
||||
"jsdom": "^27.0.0",
|
||||
"knip": "^5.83.1",
|
||||
"postcss": "^8.4.33",
|
||||
"prettier": "3.2.5",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "5.3.3",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^7.1.11",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
|
||||
@ -1,134 +0,0 @@
|
||||
import { Select, SelectItem, Text } from "@tremor/react";
|
||||
import React, { useState } from "react";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { Team } from "@/components/key_team_helpers/key_list";
|
||||
|
||||
interface FilterByContentProps {
|
||||
setSelectedAPIKey: (key: any) => void;
|
||||
keys: any[] | null;
|
||||
teams: Team[] | null;
|
||||
setSelectedCustomer: (customer: string | null) => void;
|
||||
allEndUsers: any[];
|
||||
}
|
||||
|
||||
const FilterByContent = ({
|
||||
setSelectedAPIKey,
|
||||
keys,
|
||||
teams,
|
||||
setSelectedCustomer,
|
||||
allEndUsers,
|
||||
}: FilterByContentProps) => {
|
||||
const { premiumUser } = useAuthorized();
|
||||
|
||||
const [selectedTeamFilter, setSelectedTeamFilter] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Text className="mb-1">Select API Key Name</Text>
|
||||
|
||||
{premiumUser ? (
|
||||
<div>
|
||||
<Select defaultValue="all-keys">
|
||||
<SelectItem
|
||||
key="all-keys"
|
||||
value="all-keys"
|
||||
onClick={() => {
|
||||
setSelectedAPIKey(null);
|
||||
}}
|
||||
>
|
||||
All Keys
|
||||
</SelectItem>
|
||||
{keys?.map((key: any, index: number) => {
|
||||
if (key && key["key_alias"] !== null && key["key_alias"].length > 0) {
|
||||
return (
|
||||
<SelectItem
|
||||
key={index}
|
||||
value={String(index)}
|
||||
onClick={() => {
|
||||
setSelectedAPIKey(key);
|
||||
}}
|
||||
>
|
||||
{key["key_alias"]}
|
||||
</SelectItem>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</Select>
|
||||
|
||||
<Text className="mt-1">Select Customer Name</Text>
|
||||
|
||||
<Select defaultValue="all-customers">
|
||||
<SelectItem
|
||||
key="all-customers"
|
||||
value="all-customers"
|
||||
onClick={() => {
|
||||
setSelectedCustomer(null);
|
||||
}}
|
||||
>
|
||||
All Customers
|
||||
</SelectItem>
|
||||
{allEndUsers?.map((user: any, index: number) => {
|
||||
return (
|
||||
<SelectItem
|
||||
key={index}
|
||||
value={user}
|
||||
onClick={() => {
|
||||
setSelectedCustomer(user);
|
||||
}}
|
||||
>
|
||||
{user}
|
||||
</SelectItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
|
||||
<Text className="mt-1">Select Team</Text>
|
||||
|
||||
<Select
|
||||
className="w-64 relative z-50"
|
||||
defaultValue="all"
|
||||
value={selectedTeamFilter ?? "all"}
|
||||
onValueChange={(value) => setSelectedTeamFilter(value === "all" ? null : value)}
|
||||
>
|
||||
<SelectItem value="all">All Teams</SelectItem>
|
||||
{teams
|
||||
?.filter((team) => team.team_id)
|
||||
.map((team) => (
|
||||
<SelectItem key={team.team_id} value={team.team_id}>
|
||||
{team.team_alias
|
||||
? `${team.team_alias} (${team.team_id.slice(0, 8)}...)`
|
||||
: `Team ${team.team_id.slice(0, 8)}...`}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{/* ... existing non-premium user content ... */}
|
||||
<Text className="mt-1">Select Team</Text>
|
||||
|
||||
<Select
|
||||
className="w-64 relative z-50"
|
||||
defaultValue="all"
|
||||
value={selectedTeamFilter ?? "all"}
|
||||
onValueChange={(value) => setSelectedTeamFilter(value === "all" ? null : value)}
|
||||
>
|
||||
<SelectItem value="all">All Teams</SelectItem>
|
||||
{teams
|
||||
?.filter((team) => team.team_id)
|
||||
.map((team) => (
|
||||
<SelectItem key={team.team_id} value={team.team_id}>
|
||||
{team.team_alias
|
||||
? `${team.team_alias} (${team.team_id.slice(0, 8)}...)`
|
||||
: `Team ${team.team_id.slice(0, 8)}...`}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FilterByContent;
|
||||
@ -1,474 +0,0 @@
|
||||
import {
|
||||
AreaChart,
|
||||
BarChart,
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
DateRangePickerValue,
|
||||
Grid,
|
||||
Select,
|
||||
SelectItem,
|
||||
Subtitle,
|
||||
Tab,
|
||||
TabGroup,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Text,
|
||||
Title,
|
||||
} from "@tremor/react";
|
||||
import UsageDatePicker from "@/components/shared/usage_date_picker";
|
||||
import { Popover } from "antd";
|
||||
import { FilterIcon } from "@heroicons/react/outline";
|
||||
import TimeToFirstToken from "@/components/model_metrics/time_to_first_token";
|
||||
import React, { useEffect } from "react";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { Team } from "@/components/key_team_helpers/key_list";
|
||||
import {
|
||||
adminGlobalActivityExceptions,
|
||||
adminGlobalActivityExceptionsPerDeployment,
|
||||
modelExceptionsCall,
|
||||
modelMetricsCall,
|
||||
modelMetricsSlowResponsesCall,
|
||||
streamingModelMetricsCall,
|
||||
} from "@/components/networking";
|
||||
import FilterByContent from "@/app/(dashboard)/models-and-endpoints/components/ModelAnalyticsTab/FilterByContent";
|
||||
|
||||
interface GlobalExceptionActivityData {
|
||||
sum_num_rate_limit_exceptions: number;
|
||||
daily_data: { date: string; num_rate_limit_exceptions: number }[];
|
||||
}
|
||||
|
||||
interface ModelAnalyticsTabProps {
|
||||
dateValue: DateRangePickerValue;
|
||||
setDateValue: (dateValue: DateRangePickerValue) => void;
|
||||
selectedModelGroup: string | null;
|
||||
availableModelGroups: string[];
|
||||
setShowAdvancedFilters: (showAdvancedFilters: boolean) => void;
|
||||
modelMetrics: any[];
|
||||
modelMetricsCategories: any[];
|
||||
streamingModelMetrics: any[];
|
||||
streamingModelMetricsCategories: any[];
|
||||
customTooltip: any;
|
||||
slowResponsesData: any[];
|
||||
modelExceptions: any[];
|
||||
globalExceptionData: GlobalExceptionActivityData;
|
||||
allExceptions: any[];
|
||||
globalExceptionPerDeployment: any[];
|
||||
setSelectedAPIKey: (key: string | null) => void;
|
||||
keys: any[] | null;
|
||||
setSelectedCustomer: (selectedCustomer: string | null) => void;
|
||||
teams: Team[] | null;
|
||||
allEndUsers: any[];
|
||||
selectedAPIKey: any;
|
||||
selectedCustomer: string | null;
|
||||
selectedTeam: string | null;
|
||||
setSelectedModelGroup: (selectedModelGroup: string | null) => void;
|
||||
setModelMetrics: (metrics: any) => void;
|
||||
setModelMetricsCategories: (categories: any) => void;
|
||||
setStreamingModelMetrics: (metrics: any) => void;
|
||||
setStreamingModelMetricsCategories: (categories: any) => void;
|
||||
setSlowResponsesData: (data: any) => void;
|
||||
setModelExceptions: (exceptions: any) => void;
|
||||
setAllExceptions: (exceptions: any) => void;
|
||||
setGlobalExceptionData: (data: any) => void;
|
||||
setGlobalExceptionPerDeployment: (data: any) => void;
|
||||
}
|
||||
|
||||
const ModelAnalyticsTab = ({
|
||||
dateValue,
|
||||
setDateValue,
|
||||
selectedModelGroup,
|
||||
availableModelGroups,
|
||||
setShowAdvancedFilters,
|
||||
modelMetrics,
|
||||
modelMetricsCategories,
|
||||
streamingModelMetrics,
|
||||
streamingModelMetricsCategories,
|
||||
customTooltip,
|
||||
slowResponsesData,
|
||||
modelExceptions,
|
||||
globalExceptionData,
|
||||
allExceptions,
|
||||
globalExceptionPerDeployment,
|
||||
setSelectedAPIKey,
|
||||
keys,
|
||||
setSelectedCustomer,
|
||||
teams,
|
||||
allEndUsers,
|
||||
selectedAPIKey,
|
||||
selectedCustomer,
|
||||
selectedTeam,
|
||||
setSelectedModelGroup,
|
||||
setModelMetrics,
|
||||
setModelMetricsCategories,
|
||||
setStreamingModelMetrics,
|
||||
setStreamingModelMetricsCategories,
|
||||
setSlowResponsesData,
|
||||
setModelExceptions,
|
||||
setAllExceptions,
|
||||
setGlobalExceptionData,
|
||||
setGlobalExceptionPerDeployment,
|
||||
}: ModelAnalyticsTabProps) => {
|
||||
const { accessToken, userId, userRole, premiumUser } = useAuthorized();
|
||||
|
||||
useEffect(() => {
|
||||
updateModelMetrics(selectedModelGroup, dateValue.from, dateValue.to);
|
||||
}, [selectedAPIKey, selectedCustomer, selectedTeam]);
|
||||
|
||||
const updateModelMetrics = async (
|
||||
modelGroup: string | null,
|
||||
startTime: Date | undefined,
|
||||
endTime: Date | undefined,
|
||||
) => {
|
||||
console.log("Updating model metrics for group:", modelGroup);
|
||||
if (!accessToken || !userId || !userRole || !startTime || !endTime) {
|
||||
return;
|
||||
}
|
||||
console.log("inside updateModelMetrics - startTime:", startTime, "endTime:", endTime);
|
||||
setSelectedModelGroup(modelGroup);
|
||||
|
||||
let selected_token = selectedAPIKey?.token;
|
||||
if (selected_token === undefined) {
|
||||
selected_token = null;
|
||||
}
|
||||
|
||||
let selected_customer = selectedCustomer;
|
||||
if (selected_customer === undefined) {
|
||||
selected_customer = null;
|
||||
}
|
||||
|
||||
try {
|
||||
const modelMetricsResponse = await modelMetricsCall(
|
||||
accessToken,
|
||||
userId,
|
||||
userRole,
|
||||
modelGroup,
|
||||
startTime.toISOString(),
|
||||
endTime.toISOString(),
|
||||
selected_token,
|
||||
selected_customer,
|
||||
);
|
||||
console.log("Model metrics response:", modelMetricsResponse);
|
||||
|
||||
// Assuming modelMetricsResponse now contains the metric data for the specified model group
|
||||
setModelMetrics(modelMetricsResponse.data);
|
||||
setModelMetricsCategories(modelMetricsResponse.all_api_bases);
|
||||
|
||||
const streamingModelMetricsResponse = await streamingModelMetricsCall(
|
||||
accessToken,
|
||||
modelGroup,
|
||||
startTime.toISOString(),
|
||||
endTime.toISOString(),
|
||||
);
|
||||
|
||||
// Assuming modelMetricsResponse now contains the metric data for the specified model group
|
||||
setStreamingModelMetrics(streamingModelMetricsResponse.data);
|
||||
setStreamingModelMetricsCategories(streamingModelMetricsResponse.all_api_bases);
|
||||
|
||||
const modelExceptionsResponse = await modelExceptionsCall(
|
||||
accessToken,
|
||||
userId,
|
||||
userRole,
|
||||
modelGroup,
|
||||
startTime.toISOString(),
|
||||
endTime.toISOString(),
|
||||
selected_token,
|
||||
selected_customer,
|
||||
);
|
||||
console.log("Model exceptions response:", modelExceptionsResponse);
|
||||
setModelExceptions(modelExceptionsResponse.data);
|
||||
setAllExceptions(modelExceptionsResponse.exception_types);
|
||||
|
||||
const slowResponses = await modelMetricsSlowResponsesCall(
|
||||
accessToken,
|
||||
userId,
|
||||
userRole,
|
||||
modelGroup,
|
||||
startTime.toISOString(),
|
||||
endTime.toISOString(),
|
||||
selected_token,
|
||||
selected_customer,
|
||||
);
|
||||
|
||||
console.log("slowResponses:", slowResponses);
|
||||
|
||||
setSlowResponsesData(slowResponses);
|
||||
|
||||
if (modelGroup) {
|
||||
const dailyExceptions = await adminGlobalActivityExceptions(
|
||||
accessToken,
|
||||
startTime?.toISOString().split("T")[0],
|
||||
endTime?.toISOString().split("T")[0],
|
||||
modelGroup,
|
||||
);
|
||||
|
||||
setGlobalExceptionData(dailyExceptions);
|
||||
|
||||
const dailyExceptionsPerDeplyment = await adminGlobalActivityExceptionsPerDeployment(
|
||||
accessToken,
|
||||
startTime?.toISOString().split("T")[0],
|
||||
endTime?.toISOString().split("T")[0],
|
||||
modelGroup,
|
||||
);
|
||||
|
||||
setGlobalExceptionPerDeployment(dailyExceptionsPerDeplyment);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch model metrics", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TabPanel>
|
||||
<div className="mb-4 rounded-md border border-red-500 bg-red-50 p-4">
|
||||
<Text className="font-semibold text-red-700">
|
||||
This page is deprecated and will be removed in the future. Some functionality may not work as expected.
|
||||
</Text>
|
||||
</div>
|
||||
<Grid numItems={4} className="mt-2 mb-2">
|
||||
<Col>
|
||||
<UsageDatePicker
|
||||
value={dateValue}
|
||||
className="mr-2"
|
||||
onValueChange={(value) => {
|
||||
setDateValue(value);
|
||||
updateModelMetrics(selectedModelGroup, value.from, value.to);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col className="ml-2">
|
||||
<Text>Select Model Group</Text>
|
||||
<Select
|
||||
defaultValue={selectedModelGroup ? selectedModelGroup : availableModelGroups[0]}
|
||||
value={selectedModelGroup ? selectedModelGroup : availableModelGroups[0]}
|
||||
>
|
||||
{availableModelGroups.map((group, idx) => (
|
||||
<SelectItem
|
||||
key={idx}
|
||||
value={group}
|
||||
onClick={() => updateModelMetrics(group, dateValue.from, dateValue.to)}
|
||||
>
|
||||
{group}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col>
|
||||
<Popover
|
||||
trigger="click"
|
||||
content={
|
||||
<FilterByContent
|
||||
allEndUsers={allEndUsers}
|
||||
keys={keys}
|
||||
setSelectedAPIKey={setSelectedAPIKey}
|
||||
setSelectedCustomer={setSelectedCustomer}
|
||||
teams={teams}
|
||||
/>
|
||||
}
|
||||
overlayStyle={{
|
||||
width: "20vw",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
icon={FilterIcon}
|
||||
size="md"
|
||||
variant="secondary"
|
||||
className="mt-4 ml-2"
|
||||
style={{
|
||||
border: "none",
|
||||
}}
|
||||
onClick={() => setShowAdvancedFilters(true)}
|
||||
></Button>
|
||||
</Popover>
|
||||
</Col>
|
||||
</Grid>
|
||||
|
||||
<Grid numItems={2}>
|
||||
<Col>
|
||||
<Card className="mr-2 max-h-[400px] min-h-[400px]">
|
||||
<TabGroup>
|
||||
<TabList variant="line" defaultValue="1">
|
||||
<Tab value="1">Avg. Latency per Token</Tab>
|
||||
<Tab value="2">Time to first token</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<p className="text-gray-500 italic"> (seconds/token)</p>
|
||||
<Text className="text-gray-500 italic mt-1 mb-1">
|
||||
average Latency for successfull requests divided by the total tokens
|
||||
</Text>
|
||||
{modelMetrics && modelMetricsCategories && (
|
||||
<AreaChart
|
||||
title="Model Latency"
|
||||
className="h-72"
|
||||
data={modelMetrics}
|
||||
showLegend={false}
|
||||
index="date"
|
||||
categories={modelMetricsCategories}
|
||||
connectNulls={true}
|
||||
customTooltip={customTooltip}
|
||||
/>
|
||||
)}
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<TimeToFirstToken
|
||||
modelMetrics={streamingModelMetrics}
|
||||
modelMetricsCategories={streamingModelMetricsCategories}
|
||||
customTooltip={customTooltip}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col>
|
||||
<Card className="ml-2 max-h-[400px] min-h-[400px] overflow-y-auto">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>Deployment</TableHeaderCell>
|
||||
<TableHeaderCell>Success Responses</TableHeaderCell>
|
||||
<TableHeaderCell>
|
||||
Slow Responses <p>Success Responses taking 600+s</p>
|
||||
</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{slowResponsesData.map((metric, idx) => (
|
||||
<TableRow key={idx}>
|
||||
<TableCell>{metric.api_base}</TableCell>
|
||||
<TableCell>{metric.total_count}</TableCell>
|
||||
<TableCell>{metric.slow_count}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</Col>
|
||||
</Grid>
|
||||
<Grid numItems={1} className="gap-2 w-full mt-2">
|
||||
<Card>
|
||||
<Title>All Exceptions for {selectedModelGroup}</Title>
|
||||
|
||||
<BarChart
|
||||
className="h-60"
|
||||
data={modelExceptions}
|
||||
index="model"
|
||||
categories={allExceptions}
|
||||
stack={true}
|
||||
yAxisWidth={30}
|
||||
/>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
<Grid numItems={1} className="gap-2 w-full mt-2">
|
||||
<Card>
|
||||
<Title>All Up Rate Limit Errors (429) for {selectedModelGroup}</Title>
|
||||
<Grid numItems={1}>
|
||||
<Col>
|
||||
<Subtitle
|
||||
style={{
|
||||
fontSize: "15px",
|
||||
fontWeight: "normal",
|
||||
color: "#535452",
|
||||
}}
|
||||
>
|
||||
Num Rate Limit Errors {globalExceptionData.sum_num_rate_limit_exceptions}
|
||||
</Subtitle>
|
||||
<BarChart
|
||||
className="h-40"
|
||||
data={globalExceptionData.daily_data}
|
||||
index="date"
|
||||
colors={["rose"]}
|
||||
categories={["num_rate_limit_exceptions"]}
|
||||
onValueChange={(v) => console.log(v)}
|
||||
/>
|
||||
</Col>
|
||||
<Col></Col>
|
||||
</Grid>
|
||||
</Card>
|
||||
|
||||
{premiumUser ? (
|
||||
<>
|
||||
{globalExceptionPerDeployment.map((globalActivity, index) => (
|
||||
<Card key={index}>
|
||||
<Title>{globalActivity.api_base ? globalActivity.api_base : "Unknown API Base"}</Title>
|
||||
<Grid numItems={1}>
|
||||
<Col>
|
||||
<Subtitle
|
||||
style={{
|
||||
fontSize: "15px",
|
||||
fontWeight: "normal",
|
||||
color: "#535452",
|
||||
}}
|
||||
>
|
||||
Num Rate Limit Errors (429) {globalActivity.sum_num_rate_limit_exceptions}
|
||||
</Subtitle>
|
||||
<BarChart
|
||||
className="h-40"
|
||||
data={globalActivity.daily_data}
|
||||
index="date"
|
||||
colors={["rose"]}
|
||||
categories={["num_rate_limit_exceptions"]}
|
||||
onValueChange={(v) => console.log(v)}
|
||||
/>
|
||||
</Col>
|
||||
</Grid>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{globalExceptionPerDeployment &&
|
||||
globalExceptionPerDeployment.length > 0 &&
|
||||
globalExceptionPerDeployment.slice(0, 1).map((globalActivity, index) => (
|
||||
<Card key={index}>
|
||||
<Title>✨ Rate Limit Errors by Deployment</Title>
|
||||
<p className="mb-2 text-gray-500 italic text-[12px]">Upgrade to see exceptions for all deployments</p>
|
||||
<Button variant="primary" className="mb-2">
|
||||
<a href="https://forms.gle/W3U4PZpJGFHWtHyA9" target="_blank">
|
||||
Get Free Trial
|
||||
</a>
|
||||
</Button>
|
||||
<Card>
|
||||
<Title>{globalActivity.api_base}</Title>
|
||||
<Grid numItems={1}>
|
||||
<Col>
|
||||
<Subtitle
|
||||
style={{
|
||||
fontSize: "15px",
|
||||
fontWeight: "normal",
|
||||
color: "#535452",
|
||||
}}
|
||||
>
|
||||
Num Rate Limit Errors {globalActivity.sum_num_rate_limit_exceptions}
|
||||
</Subtitle>
|
||||
<BarChart
|
||||
className="h-40"
|
||||
data={globalActivity.daily_data}
|
||||
index="date"
|
||||
colors={["rose"]}
|
||||
categories={["num_rate_limit_exceptions"]}
|
||||
onValueChange={(v) => console.log(v)}
|
||||
/>
|
||||
</Col>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelAnalyticsTab;
|
||||
@ -1,153 +0,0 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
Text,
|
||||
Badge,
|
||||
Icon,
|
||||
Card,
|
||||
} from "@tremor/react";
|
||||
import { Tooltip } from "antd";
|
||||
|
||||
interface Column {
|
||||
header: string;
|
||||
accessor: string;
|
||||
cellRenderer?: (value: any, row: any) => React.ReactNode;
|
||||
width?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
interface Action<T = any> {
|
||||
icon?: React.ComponentType<any>;
|
||||
onClick: (item: T) => void;
|
||||
condition?: () => boolean;
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
interface DeleteModalProps {
|
||||
isOpen: boolean;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
title: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface DataTableProps {
|
||||
data: any[];
|
||||
columns: Column[];
|
||||
actions?: Action[];
|
||||
emptyMessage?: string;
|
||||
deleteModal?: DeleteModalProps;
|
||||
onItemClick?: (item: any) => void;
|
||||
}
|
||||
|
||||
const DataTable: React.FC<DataTableProps> = ({
|
||||
data,
|
||||
columns,
|
||||
actions,
|
||||
emptyMessage = "No data available",
|
||||
deleteModal,
|
||||
onItemClick,
|
||||
}) => {
|
||||
const renderCell = (column: Column, row: any) => {
|
||||
const value = row[column.accessor];
|
||||
|
||||
if (column.cellRenderer) {
|
||||
return column.cellRenderer(value, row);
|
||||
}
|
||||
|
||||
// Default cell rendering based on value type
|
||||
if (Array.isArray(value)) {
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
{value.length === 0 ? (
|
||||
<Badge size="xs" className="mb-1" color="red">
|
||||
<Text>None</Text>
|
||||
</Badge>
|
||||
) : (
|
||||
value.map((item: any, index: number) => (
|
||||
<Badge key={index} size="xs" className="mb-1" color="blue">
|
||||
<Text>{String(item).length > 30 ? `${String(item).slice(0, 30)}...` : item}</Text>
|
||||
</Badge>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return value?.toString() || "";
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="w-full mx-auto flex-auto overflow-y-auto max-h-[40vh]">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{columns.map((column, index) => (
|
||||
<TableHeaderCell key={index}>{column.header}</TableHeaderCell>
|
||||
))}
|
||||
{actions && actions.length > 0 && <TableHeaderCell>Actions</TableHeaderCell>}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
<TableBody>
|
||||
{data && data.length > 0 ? (
|
||||
data.map((row, rowIndex) => (
|
||||
<TableRow key={rowIndex}>
|
||||
{columns.map((column, colIndex) => (
|
||||
<TableCell
|
||||
key={colIndex}
|
||||
style={{
|
||||
maxWidth: column.width || "4px",
|
||||
whiteSpace: "pre-wrap",
|
||||
overflow: "hidden",
|
||||
...column.style,
|
||||
}}
|
||||
>
|
||||
{column.accessor === "id" ? (
|
||||
<Tooltip title={row[column.accessor]}>{renderCell(column, row)}</Tooltip>
|
||||
) : (
|
||||
renderCell(column, row)
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
{actions && actions.length > 0 && (
|
||||
<TableCell>
|
||||
{actions.map(
|
||||
(action, actionIndex) =>
|
||||
// @ts-ignore
|
||||
action.condition?.(row) !== false && (
|
||||
<Tooltip key={actionIndex} title={action.tooltip}>
|
||||
<Icon
|
||||
// @ts-ignore
|
||||
icon={action.icon}
|
||||
size="sm"
|
||||
onClick={() => action.onClick(row)}
|
||||
className="cursor-pointer mx-1"
|
||||
/>
|
||||
</Tooltip>
|
||||
),
|
||||
)}
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length + (actions ? 1 : 0)}>
|
||||
<Text className="text-center">{emptyMessage}</Text>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataTable;
|
||||
export type { Action, Column, DataTableProps, DeleteModalProps };
|
||||
@ -1,6 +0,0 @@
|
||||
import { Organization } from "../networking";
|
||||
|
||||
export const defaultOrg = {
|
||||
organization_id: "default_organization",
|
||||
organization_alias: "Default Organization",
|
||||
} as Organization;
|
||||
@ -1,59 +0,0 @@
|
||||
import React from "react";
|
||||
import { Select, TextInput } from "@tremor/react";
|
||||
import { Form, Select as AntSelect } from "antd";
|
||||
import TeamDropdown from "./team_dropdown";
|
||||
import { getPossibleUserRoles } from "../networking";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
|
||||
interface UserFormProps {
|
||||
form: any;
|
||||
teams: any[] | null;
|
||||
possibleUIRoles: null | Record<string, Record<string, string>>;
|
||||
setPossibleUIRoles?: (roles: any) => void;
|
||||
accessToken?: string;
|
||||
}
|
||||
|
||||
const UserForm: React.FC<UserFormProps> = ({ form, teams, possibleUIRoles, setPossibleUIRoles, accessToken }) => {
|
||||
React.useEffect(() => {
|
||||
// Fetch roles if they're not available and we have a setter
|
||||
if (!possibleUIRoles && setPossibleUIRoles && accessToken) {
|
||||
getPossibleUserRoles(accessToken).then((roles) => {
|
||||
setPossibleUIRoles(roles);
|
||||
});
|
||||
}
|
||||
}, [possibleUIRoles, setPossibleUIRoles, accessToken]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item label="User Email" name="user_email" rules={[{ required: true, message: "Please input user email" }]}>
|
||||
<TextInput placeholder="" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="User Role" name="user_role" rules={[{ required: true, message: "Please select a role" }]}>
|
||||
<Select>
|
||||
{possibleUIRoles &&
|
||||
Object.entries(possibleUIRoles).map(([role, { ui_label, description }]) => (
|
||||
<AntSelect.Option key={role} value={role} title={ui_label}>
|
||||
<div className="flex">
|
||||
{ui_label}{" "}
|
||||
<p className="ml-2" style={{ color: "gray", fontSize: "12px" }}>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</AntSelect.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Team" name="team_id">
|
||||
<TeamDropdown teams={teams} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Metadata" name="metadata">
|
||||
<TextArea rows={4} placeholder="Enter metadata as JSON" />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserForm;
|
||||
@ -1,55 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { Grid, Col, Icon } from "@tremor/react";
|
||||
import { Title } from "@tremor/react";
|
||||
import { Modal } from "antd";
|
||||
import { modelDeleteCall } from "./networking";
|
||||
import { TrashIcon } from "@heroicons/react/outline";
|
||||
import NotificationsManager from "./molecules/notifications_manager";
|
||||
interface DeleteModelProps {
|
||||
modelID: string;
|
||||
accessToken: string;
|
||||
callback?: () => void;
|
||||
}
|
||||
|
||||
const DeleteModelButton: React.FC<DeleteModelProps> = ({ modelID, accessToken, callback }) => {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
NotificationsManager.info("Making API Call");
|
||||
setIsModalVisible(true);
|
||||
const response = await modelDeleteCall(accessToken, modelID);
|
||||
|
||||
console.log("model delete Response:", response);
|
||||
NotificationsManager.success(`Model ${modelID} deleted successfully`);
|
||||
setIsModalVisible(false);
|
||||
callback && setTimeout(callback, 4000); //added timeout of 4 seconds as deleted model is taking time to reflect in get models
|
||||
} catch (error) {
|
||||
console.error("Error deleting the model:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Icon onClick={() => setIsModalVisible(true)} icon={TrashIcon} size="sm" />
|
||||
|
||||
<Modal open={isModalVisible} onOk={handleDelete} okType="danger" onCancel={() => setIsModalVisible(false)}>
|
||||
<Grid numItems={1} className="gap-2 w-full">
|
||||
<Title>Delete Model</Title>
|
||||
<Col numColSpan={1}>
|
||||
<p>Are you sure you want to delete this model? This action is irreversible.</p>
|
||||
</Col>
|
||||
<Col numColSpan={1}>
|
||||
<p>
|
||||
Model ID: <b>{modelID}</b>
|
||||
</p>
|
||||
</Col>
|
||||
</Grid>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteModelButton;
|
||||
@ -1,214 +0,0 @@
|
||||
import React from "react";
|
||||
import { Modal, Form, InputNumber } from "antd";
|
||||
import { TextInput } from "@tremor/react";
|
||||
import { Button as Button2 } from "antd";
|
||||
import { modelUpdateCall } from "../networking";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
interface EditModelModalProps {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
model: any;
|
||||
onSubmit: (data: FormData) => void;
|
||||
}
|
||||
|
||||
export const handleEditModelSubmit = async (
|
||||
formValues: Record<string, any>,
|
||||
accessToken: string | null,
|
||||
setEditModalVisible: (visible: boolean) => void,
|
||||
setSelectedModel: (model: any) => void,
|
||||
) => {
|
||||
// Call API to update team with teamId and values
|
||||
|
||||
console.log("handleEditSubmit:", formValues);
|
||||
if (accessToken == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newLiteLLMParams: Record<string, any> = {};
|
||||
let model_info_model_id = null;
|
||||
|
||||
if (formValues.input_cost_per_token) {
|
||||
// Convert from per 1M tokens to per token
|
||||
formValues.input_cost_per_token = Number(formValues.input_cost_per_token) / 1_000_000;
|
||||
}
|
||||
if (formValues.output_cost_per_token) {
|
||||
// Convert from per 1M tokens to per token
|
||||
formValues.output_cost_per_token = Number(formValues.output_cost_per_token) / 1_000_000;
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(formValues)) {
|
||||
if (key !== "model_id") {
|
||||
// Empty string means user wants to null the value
|
||||
newLiteLLMParams[key] = value === "" ? null : value;
|
||||
} else {
|
||||
model_info_model_id = value === "" ? null : value;
|
||||
}
|
||||
}
|
||||
|
||||
let payload: {
|
||||
litellm_params: Record<string, any> | undefined;
|
||||
model_info: { id: any } | undefined;
|
||||
} = {
|
||||
litellm_params: Object.keys(newLiteLLMParams).length > 0 ? newLiteLLMParams : undefined,
|
||||
model_info:
|
||||
model_info_model_id !== undefined
|
||||
? {
|
||||
id: model_info_model_id,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
console.log("handleEditSubmit payload:", payload);
|
||||
|
||||
try {
|
||||
let newModelValue = await modelUpdateCall(accessToken, payload);
|
||||
NotificationsManager.success("Model updated successfully, restart server to see updates");
|
||||
|
||||
setEditModalVisible(false);
|
||||
setSelectedModel(null);
|
||||
} catch (error) {
|
||||
console.log(`Error occurred`);
|
||||
}
|
||||
};
|
||||
|
||||
const EditModelModal: React.FC<EditModelModalProps> = ({ visible, onCancel, model, onSubmit }) => {
|
||||
const [form] = Form.useForm();
|
||||
let litellm_params_to_edit: Record<string, any> = {};
|
||||
let model_name = "";
|
||||
let model_id = "";
|
||||
if (model) {
|
||||
litellm_params_to_edit = {
|
||||
...model.litellm_params,
|
||||
input_cost_per_token: model.litellm_params?.input_cost_per_token
|
||||
? model.litellm_params.input_cost_per_token * 1_000_000
|
||||
: undefined,
|
||||
output_cost_per_token: model.litellm_params?.output_cost_per_token
|
||||
? model.litellm_params.output_cost_per_token * 1_000_000
|
||||
: undefined,
|
||||
};
|
||||
model_name = model.model_name;
|
||||
let model_info = model.model_info;
|
||||
if (model_info) {
|
||||
model_id = model_info.id;
|
||||
console.log(`model_id: ${model_id}`);
|
||||
litellm_params_to_edit.model_id = model_id;
|
||||
}
|
||||
}
|
||||
|
||||
const handleOk = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
const submissionValues = {
|
||||
...values,
|
||||
input_cost_per_token: values.input_cost_per_token
|
||||
? Number(values.input_cost_per_token) / 1_000_000
|
||||
: undefined,
|
||||
output_cost_per_token: values.output_cost_per_token
|
||||
? Number(values.output_cost_per_token) / 1_000_000
|
||||
: undefined,
|
||||
};
|
||||
onSubmit(submissionValues);
|
||||
form.resetFields();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Validation failed:", error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={"Edit '" + model_name + "' LiteLLM Params"}
|
||||
open={visible}
|
||||
width={800}
|
||||
footer={null}
|
||||
onOk={handleOk}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onSubmit}
|
||||
initialValues={litellm_params_to_edit}
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
labelAlign="left"
|
||||
>
|
||||
<>
|
||||
<Form.Item
|
||||
label="Input Cost (per 1M tokens)"
|
||||
name="input_cost_per_token"
|
||||
tooltip="float (optional) - Input cost per 1 million tokens"
|
||||
>
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Output Cost (per 1M tokens)"
|
||||
name="output_cost_per_token"
|
||||
tooltip="float (optional) - Output cost per 1 million tokens"
|
||||
>
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
<Form.Item className="mt-8" label="api_base" name="api_base">
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
<Form.Item className="mt-8" label="api_key" name="api_key">
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
<Form.Item className="mt-8" label="custom_llm_provider" name="custom_llm_provider">
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
<Form.Item className="mt-8" label="model" name="model">
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
<Form.Item label="organization" name="organization" tooltip="OpenAI Organization ID">
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="tpm"
|
||||
name="tpm"
|
||||
tooltip="int (optional) - Tokens limit for this deployment: in tokens per minute (tpm). Find this information on your model/providers website"
|
||||
>
|
||||
<InputNumber min={0} step={1} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="rpm"
|
||||
name="rpm"
|
||||
tooltip="int (optional) - Rate limit for this deployment: in requests per minute (rpm). Find this information on your model/providers website"
|
||||
>
|
||||
<InputNumber min={0} step={1} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="max_retries" name="max_retries">
|
||||
<InputNumber min={0} step={1} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="timeout"
|
||||
name="timeout"
|
||||
tooltip="int (optional) - Timeout in seconds for LLM requests (Defaults to 600 seconds)"
|
||||
>
|
||||
<InputNumber min={0} step={1} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="stream_timeout"
|
||||
name="stream_timeout"
|
||||
tooltip="int (optional) - Timeout for stream requests (seconds)"
|
||||
>
|
||||
<InputNumber min={0} step={1} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="model_id" name="model_id" hidden={true}></Form.Item>
|
||||
</>
|
||||
<div style={{ textAlign: "right", marginTop: "10px" }}>
|
||||
<Button2 htmlType="submit">Save</Button2>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditModelModal;
|
||||
@ -1,23 +0,0 @@
|
||||
import { Organization } from "../networking";
|
||||
|
||||
export const createOrgSearchFunction = (organizations: Organization[] | null) => {
|
||||
return async (searchText: string): Promise<Array<{ label: string; value: string }>> => {
|
||||
if (!organizations || !searchText.trim()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Find organizations that match the search text by alias
|
||||
const matchingOrgs: Array<{ label: string; value: string }> = [];
|
||||
|
||||
organizations.forEach((org) => {
|
||||
if (org.organization_alias && org.organization_alias.toLowerCase().includes(searchText.toLowerCase())) {
|
||||
matchingOrgs.push({
|
||||
label: `${org.organization_alias} (${org.organization_id})`,
|
||||
value: org.organization_id || "",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return matchingOrgs;
|
||||
};
|
||||
};
|
||||
@ -1,113 +0,0 @@
|
||||
/**
|
||||
* This component is used to add an admin to an organization.
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import { Button, Col, Text } from "@tremor/react";
|
||||
import { Button as Button2, Select as Select2, Modal, Form, Input } from "antd";
|
||||
import { Organization } from "@/components/organization/types";
|
||||
interface AddOrgAdminProps {
|
||||
userRole: string;
|
||||
userID: string;
|
||||
selectedOrganization?: Organization;
|
||||
onMemberAdd?: (formValues: Record<string, any>) => void;
|
||||
}
|
||||
|
||||
const is_org_admin = (organization: any, userID: string) => {
|
||||
for (let i = 0; i < organization.members_with_roles.length; i++) {
|
||||
let member = organization.members_with_roles[i];
|
||||
if (member.user_id == userID && member.role == "admin") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const AddOrgAdmin: FC<AddOrgAdminProps> = ({ userRole, userID, selectedOrganization, onMemberAdd }) => {
|
||||
const [isAddMemberModalVisible, setIsAddMemberModalVisible] = React.useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const handleMemberCancel = () => {
|
||||
form.resetFields();
|
||||
setIsAddMemberModalVisible(false);
|
||||
};
|
||||
|
||||
const handleMemberOk = () => {
|
||||
form.submit();
|
||||
};
|
||||
|
||||
return (
|
||||
<Col numColSpan={1}>
|
||||
{userRole === "Admin" || (selectedOrganization && is_org_admin(selectedOrganization, userID)) ? (
|
||||
<Button className="mx-auto mb-5" onClick={() => setIsAddMemberModalVisible(true)}>
|
||||
+ Add member
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
<Modal
|
||||
title="Add member"
|
||||
open={isAddMemberModalVisible}
|
||||
width={800}
|
||||
footer={null}
|
||||
onOk={handleMemberOk}
|
||||
onCancel={handleMemberCancel}
|
||||
>
|
||||
<Text className="mb-2">User must exist in proxy. Get User ID from 'Users' tab.</Text>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onMemberAdd}
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
labelAlign="left"
|
||||
initialValues={{
|
||||
role: "internal_user",
|
||||
}}
|
||||
>
|
||||
<Form.Item label="Email" name="user_email" className="mb-4">
|
||||
<Input name="user_email" className="px-3 py-2 border rounded-md w-full" />
|
||||
</Form.Item>
|
||||
|
||||
<div className="text-center mb-4">OR</div>
|
||||
|
||||
<Form.Item label="User ID" name="user_id" className="mb-4">
|
||||
<Input name="user_id" className="px-3 py-2 border rounded-md w-full" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Member Role" name="role" className="mb-4">
|
||||
<Select2 defaultValue="user">
|
||||
<Select2.Option value="org_admin">
|
||||
<div className="flex">
|
||||
Org Admin{" "}
|
||||
<p className="ml-2" style={{ color: "gray", fontSize: "12px" }}>
|
||||
Can add and remove members, and change their roles.
|
||||
</p>
|
||||
</div>
|
||||
</Select2.Option>
|
||||
<Select2.Option value="internal_user">
|
||||
<div className="flex">
|
||||
Internal User{" "}
|
||||
<p className="ml-2" style={{ color: "gray", fontSize: "12px" }}>
|
||||
Can view/create keys for themselves within organization.
|
||||
</p>
|
||||
</div>
|
||||
</Select2.Option>
|
||||
<Select2.Option value="internal_user_viewer">
|
||||
<div className="flex">
|
||||
Internal User Viewer{" "}
|
||||
<p className="ml-2" style={{ color: "gray", fontSize: "12px" }}>
|
||||
Can only view their keys within organization.
|
||||
</p>
|
||||
</div>
|
||||
</Select2.Option>
|
||||
</Select2>
|
||||
</Form.Item>
|
||||
|
||||
<div style={{ textAlign: "right", marginTop: "10px" }}>
|
||||
<Button2 htmlType="submit">Add member</Button2>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Col>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddOrgAdmin;
|
||||
@ -1,74 +0,0 @@
|
||||
import React, { FC } from "react";
|
||||
|
||||
import { Organization, EditModalProps, OrganizationMember } from "./types";
|
||||
import { Card, Col, Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react";
|
||||
|
||||
interface Member {
|
||||
user_email?: string;
|
||||
user_id?: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
interface MemberListTableProps {
|
||||
selectedEntity?: Organization;
|
||||
onEditSubmit: (entity: Organization) => void;
|
||||
editModalComponent: React.ComponentType<EditModalProps>;
|
||||
entityType: "team" | "organization";
|
||||
}
|
||||
|
||||
const MemberListTable: FC<MemberListTableProps> = ({
|
||||
selectedEntity,
|
||||
onEditSubmit,
|
||||
editModalComponent: EditModal,
|
||||
entityType,
|
||||
}) => {
|
||||
const [editModalVisible, setEditModalVisible] = React.useState(false);
|
||||
|
||||
const handleEditCancel = () => {
|
||||
setEditModalVisible(false);
|
||||
};
|
||||
|
||||
const handleEditSubmit = (entity: Organization) => {
|
||||
onEditSubmit(entity);
|
||||
setEditModalVisible(false);
|
||||
};
|
||||
|
||||
const getMemberIdentifier = (member: Member) => {
|
||||
return member.user_email || member.user_id || "Unknown Member";
|
||||
};
|
||||
|
||||
return (
|
||||
<Col numColSpan={1}>
|
||||
<Card className="w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>{entityType === "team" ? "Team Member" : "Organization Member"}</TableHeaderCell>
|
||||
<TableHeaderCell>Role</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
<TableBody>
|
||||
{(selectedEntity?.members ?? []).map((value: OrganizationMember, index: number) => (
|
||||
<TableRow key={`${value.user_id}-${index}`}>
|
||||
<TableCell>{value.user_id}</TableCell>
|
||||
<TableCell>{value.user_role}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
|
||||
{selectedEntity && (
|
||||
<EditModal
|
||||
visible={editModalVisible}
|
||||
onCancel={handleEditCancel}
|
||||
entity={selectedEntity}
|
||||
onSubmit={handleEditSubmit}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemberListTable;
|
||||
Loading…
Reference in New Issue
Block a user