From 2ea95a66501c966b2107b16c197f93a0a394789b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sat, 20 Jan 2024 16:09:40 +0100 Subject: [PATCH 01/65] nit: added urls to pyproject.toml This adds links to the various URLs from the PyPI page. --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index d8638bd4b..2eee6c8f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,11 @@ authors = ["BerriAI"] license = "MIT" readme = "README.md" +[tool.poetry.urls] +homepage = "https://litellm.ai" +repository = "https://github.com/BerriAI/litellm" +documentation = "https://docs.litellm.ai" + [tool.poetry.dependencies] python = ">=3.8.1,<3.9.7 || >3.9.7" openai = ">=1.0.0" From 1f054203bf9d2295657d9ab178262eef39bb98a0 Mon Sep 17 00:00:00 2001 From: Andres Barbaro Date: Fri, 16 Feb 2024 12:22:18 -0600 Subject: [PATCH 02/65] Add safety_settings parameter to gemini generate_content calls --- litellm/llms/gemini.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/litellm/llms/gemini.py b/litellm/llms/gemini.py index 7e98345b3..2db27aeba 100644 --- a/litellm/llms/gemini.py +++ b/litellm/llms/gemini.py @@ -121,6 +121,13 @@ def completion( ## Load Config inference_params = copy.deepcopy(optional_params) stream = inference_params.pop("stream", None) + + # Handle safety settings + safety_settings_param = inference_params.pop("safety_settings", None) + safety_settings = None + if safety_settings_param: + safety_settings = [genai.types.SafetySettingDict(x) for x in safety_settings_param] + config = litellm.GeminiConfig.get_config() for k, v in config.items(): if ( @@ -141,11 +148,13 @@ def completion( response = _model.generate_content( contents=prompt, generation_config=genai.types.GenerationConfig(**inference_params), + safety_settings=safety_settings, ) else: response = _model.generate_content( contents=prompt, generation_config=genai.types.GenerationConfig(**inference_params), + safety_settings=safety_settings, stream=True, ) return response From 880213d4a6d03c1031a70b4fa6c85b7c6b6e1d65 Mon Sep 17 00:00:00 2001 From: Lunik Date: Fri, 16 Feb 2024 22:21:11 +0100 Subject: [PATCH 03/65] =?UTF-8?q?=E2=9C=A8=20Refresh=20Helm=20chart=20stru?= =?UTF-8?q?cture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lunik --- .gitignore | 4 +- .../litellm-helm/templates/deployment-ui.yaml | 89 ------------------- .../litellm-helm/templates/ingress-ui.yaml | 61 ------------- .../litellm-helm/templates/service-ui.yaml | 17 ---- .../{litellm-helm => litellm}/.helmignore | 0 .../{litellm-helm => litellm}/Chart.lock | 0 .../{litellm-helm => litellm}/Chart.yaml | 7 +- .../{litellm-helm => litellm}/README.md | 24 ++--- .../templates/NOTES.txt | 0 .../templates/_helpers.tpl | 12 --- .../templates/configmap-litellm.yaml | 0 .../templates/deployment.yaml} | 23 ++--- .../templates/hpa.yaml | 0 .../templates/ingress.yaml} | 2 +- .../templates/secret-dbcredentials.yaml | 2 +- .../templates/secret-masterkey.yaml | 2 +- .../templates/service.yaml} | 2 +- .../templates/serviceaccount.yaml | 0 .../templates/tests/test-connection.yaml | 2 +- .../{litellm-helm => litellm}/values.yaml | 69 ++------------ docs/my-website/docs/proxy/deploy.md | 29 ++++++ 21 files changed, 60 insertions(+), 285 deletions(-) delete mode 100644 deploy/charts/litellm-helm/templates/deployment-ui.yaml delete mode 100644 deploy/charts/litellm-helm/templates/ingress-ui.yaml delete mode 100644 deploy/charts/litellm-helm/templates/service-ui.yaml rename deploy/charts/{litellm-helm => litellm}/.helmignore (100%) rename deploy/charts/{litellm-helm => litellm}/Chart.lock (100%) rename deploy/charts/{litellm-helm => litellm}/Chart.yaml (96%) rename deploy/charts/{litellm-helm => litellm}/README.md (73%) rename deploy/charts/{litellm-helm => litellm}/templates/NOTES.txt (100%) rename deploy/charts/{litellm-helm => litellm}/templates/_helpers.tpl (79%) rename deploy/charts/{litellm-helm => litellm}/templates/configmap-litellm.yaml (100%) rename deploy/charts/{litellm-helm/templates/deployment-proxy.yaml => litellm/templates/deployment.yaml} (89%) rename deploy/charts/{litellm-helm => litellm}/templates/hpa.yaml (100%) rename deploy/charts/{litellm-helm/templates/ingress-proxy.yaml => litellm/templates/ingress.yaml} (96%) rename deploy/charts/{litellm-helm => litellm}/templates/secret-dbcredentials.yaml (87%) rename deploy/charts/{litellm-helm => litellm}/templates/secret-masterkey.yaml (75%) rename deploy/charts/{litellm-helm/templates/service-proxy.yaml => litellm/templates/service.yaml} (86%) rename deploy/charts/{litellm-helm => litellm}/templates/serviceaccount.yaml (100%) rename deploy/charts/{litellm-helm => litellm}/templates/tests/test-connection.yaml (92%) rename deploy/charts/{litellm-helm => litellm}/values.yaml (80%) diff --git a/.gitignore b/.gitignore index 00cd35c5b..de1c7598f 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,7 @@ ui/litellm-dashboard/node_modules ui/litellm-dashboard/next-env.d.ts ui/litellm-dashboard/package.json ui/litellm-dashboard/package-lock.json -deploy/charts/litellm-helm/*.tgz -deploy/charts/litellm-helm/charts/* +deploy/charts/litellm/*.tgz +deploy/charts/litellm/charts/* deploy/charts/*.tgz litellm/proxy/vertex_key.json diff --git a/deploy/charts/litellm-helm/templates/deployment-ui.yaml b/deploy/charts/litellm-helm/templates/deployment-ui.yaml deleted file mode 100644 index f949e2029..000000000 --- a/deploy/charts/litellm-helm/templates/deployment-ui.yaml +++ /dev/null @@ -1,89 +0,0 @@ -{{- if .Values.ui.enabled -}} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "litellm.fullname" . }}-ui - labels: - {{- include "litellm.labels" . | nindent 4 }} -spec: - {{- if not .Values.ui.autoscaling.enabled }} - replicas: {{ .Values.ui.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "litellm.ui.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "litellm.ui.labels" . | nindent 8 }} - {{- with .Values.ui.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "litellm.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.ui.podSecurityContext | nindent 8 }} - containers: - - name: {{ include "litellm.name" . }}-ui - securityContext: - {{- toYaml .Values.ui.securityContext | nindent 12 }} - image: "{{ .Values.ui.image.repository }}:{{ .Values.ui.image.tag | default (printf "main-%s" .Chart.AppVersion) }}" - imagePullPolicy: {{ .Values.ui.image.pullPolicy }} - env: - - name: BASE_URL - value: {{ (index .Values.ui.ingress.hosts 0).host | default "example.com" }} - ports: - - name: http - containerPort: {{ .Values.ui.service.port }} - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - # Give the container time to start up. Up to 5 minutes (10 * 30 seconds) - startupProbe: - httpGet: - path: / - port: http - failureThreshold: 30 - periodSeconds: 10 - resources: - {{- toYaml .Values.ui.resources | nindent 12 }} - volumeMounts: - - name: tmp - mountPath: /tmp - {{- with .Values.ui.volumeMounts }} - {{- toYaml . | nindent 12 }} - {{- end }} - volumes: - - name: tmp - emptyDir: - sizeLimit: 500Mi - {{- with .Values.ui.volumes }} - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.ui.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.ui.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.ui.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} -{{- end -}} \ No newline at end of file diff --git a/deploy/charts/litellm-helm/templates/ingress-ui.yaml b/deploy/charts/litellm-helm/templates/ingress-ui.yaml deleted file mode 100644 index 791ccf2b0..000000000 --- a/deploy/charts/litellm-helm/templates/ingress-ui.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if .Values.ui.ingress.enabled -}} -{{- $fullName := (printf "%s%s" (include "litellm.fullname" .) "-ui") -}} -{{- $svcPort := .Values.ui.service.port -}} -{{- if and .Values.ui.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ui.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ui.ingress.annotations "kubernetes.io/ingress.class" .Values.ui.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "litellm.ui.labels" . | nindent 4 }} - {{- with .Values.ui.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ui.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ui.ingress.className }} - {{- end }} - {{- if .Values.ui.ingress.tls }} - tls: - {{- range .Values.ui.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ui.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} diff --git a/deploy/charts/litellm-helm/templates/service-ui.yaml b/deploy/charts/litellm-helm/templates/service-ui.yaml deleted file mode 100644 index 50781899d..000000000 --- a/deploy/charts/litellm-helm/templates/service-ui.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.ui.enabled -}} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "litellm.fullname" . }}-ui - labels: - {{- include "litellm.labels" . | nindent 4 }} -spec: - type: {{ .Values.ui.service.type }} - ports: - - port: {{ .Values.ui.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "litellm.ui.selectorLabels" . | nindent 4 }} -{{ end -}} \ No newline at end of file diff --git a/deploy/charts/litellm-helm/.helmignore b/deploy/charts/litellm/.helmignore similarity index 100% rename from deploy/charts/litellm-helm/.helmignore rename to deploy/charts/litellm/.helmignore diff --git a/deploy/charts/litellm-helm/Chart.lock b/deploy/charts/litellm/Chart.lock similarity index 100% rename from deploy/charts/litellm-helm/Chart.lock rename to deploy/charts/litellm/Chart.lock diff --git a/deploy/charts/litellm-helm/Chart.yaml b/deploy/charts/litellm/Chart.yaml similarity index 96% rename from deploy/charts/litellm-helm/Chart.yaml rename to deploy/charts/litellm/Chart.yaml index 80eaf87dd..6ecdebb50 100644 --- a/deploy/charts/litellm-helm/Chart.yaml +++ b/deploy/charts/litellm/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 # We can't call ourselves just "litellm" because then we couldn't publish to the # same OCI repository as the "litellm" OCI image -name: litellm-helm +name: litellm description: Call all LLM APIs using the OpenAI format # A chart can be either an 'application' or a 'library' chart. @@ -18,17 +18,16 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.2.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: v1.18.9 +appVersion: v1.24.5 dependencies: - name: "postgresql" version: ">=13.3.0" repository: oci://registry-1.docker.io/bitnamicharts condition: db.deployStandalone - diff --git a/deploy/charts/litellm-helm/README.md b/deploy/charts/litellm/README.md similarity index 73% rename from deploy/charts/litellm-helm/README.md rename to deploy/charts/litellm/README.md index bf87501b3..daba8aa68 100644 --- a/deploy/charts/litellm-helm/README.md +++ b/deploy/charts/litellm/README.md @@ -43,20 +43,6 @@ data: type: Opaque ``` -### LiteLLM Admin UI Settings - -| Name | Description | Value | -| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| `ui.enabled` | Should the LiteLLM Admin UI be deployed | `true` | -| `ui.replicaCount` | The number of LiteLLM Admin UI pods to be deployed | `1` | -| `ui.image.repository` | LiteLLM Admin UI image repository | `ghcr.io/berriai/litellm` | -| `ui.image.pullPolicy` | LiteLLM Admin UI image pull policy | `IfNotPresent` | -| `ui.image.tag` | Overrides the image tag whose default the latest version of LiteLLM at the time this chart was published. | `""` | -| `ui.imagePullSecrets` | Registry credentials for the above images. | `[]` | -| `ui.service.type` | Kubernetes Service type (e.g. `LoadBalancer`, `ClusterIP`, etc.) | `ClusterIP` | -| `ui.service.port` | TCP port that the Kubernetes Service will listen on. Also the TCP port within the Pod that the web server will listen on. | `8000` | -| `ui.ingress.*` | See [values.yaml](./values.yaml) for example settings | N/A | - ### Database Settings | Name | Description | Value | | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | @@ -86,18 +72,18 @@ type: Opaque ``` ## Accessing the Admin UI -When browsing to the URL published per the settings in `ui.ingress.*`, you will +When browsing to the URL published per the settings in `ingress.*`, you will be prompted for **Admin Configuration**. The **Proxy Endpoint** is the internal -(from the `litellm-ui` pod's perspective) URL published by the `litellm-proxy` +(from the `litellm` pod's perspective) URL published by the `-litellm` Kubernetes Service. If the deployment uses the default settings for this -service, the **Proxy Endpoint** should be set to `http://litellm-proxy:8000`. +service, the **Proxy Endpoint** should be set to `http://-litellm:8000`. The **Proxy Key** is the value specified for `masterkey` or, if a `masterkey` was not provided to the helm command line, the `masterkey` is a randomly -generated string stored in the `litellm-masterkey` Kubernetes Secret. +generated string stored in the `-litellm-masterkey` Kubernetes Secret. ```bash -kubectl -n litellm get secret litellm-masterkey -o jsonpath="{.data.masterkey}" +kubectl -n litellm get secret -litellm-masterkey -o jsonpath="{.data.masterkey}" ``` ## Admin UI Limitations diff --git a/deploy/charts/litellm-helm/templates/NOTES.txt b/deploy/charts/litellm/templates/NOTES.txt similarity index 100% rename from deploy/charts/litellm-helm/templates/NOTES.txt rename to deploy/charts/litellm/templates/NOTES.txt diff --git a/deploy/charts/litellm-helm/templates/_helpers.tpl b/deploy/charts/litellm/templates/_helpers.tpl similarity index 79% rename from deploy/charts/litellm-helm/templates/_helpers.tpl rename to deploy/charts/litellm/templates/_helpers.tpl index 7e7aa8f4c..b8893d07c 100644 --- a/deploy/charts/litellm-helm/templates/_helpers.tpl +++ b/deploy/charts/litellm/templates/_helpers.tpl @@ -41,14 +41,6 @@ app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} -{{- define "litellm.ui.labels" -}} -helm.sh/chart: {{ include "litellm.chart" . }} -{{ include "litellm.ui.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} {{/* Selector labels @@ -57,10 +49,6 @@ Selector labels app.kubernetes.io/name: {{ include "litellm.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} -{{- define "litellm.ui.selectorLabels" -}} -app.kubernetes.io/name: {{ include "litellm.name" . }}-ui -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} {{/* Create the name of the service account to use diff --git a/deploy/charts/litellm-helm/templates/configmap-litellm.yaml b/deploy/charts/litellm/templates/configmap-litellm.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/configmap-litellm.yaml rename to deploy/charts/litellm/templates/configmap-litellm.yaml diff --git a/deploy/charts/litellm-helm/templates/deployment-proxy.yaml b/deploy/charts/litellm/templates/deployment.yaml similarity index 89% rename from deploy/charts/litellm-helm/templates/deployment-proxy.yaml rename to deploy/charts/litellm/templates/deployment.yaml index cdcd207c0..6ed112dac 100644 --- a/deploy/charts/litellm-helm/templates/deployment-proxy.yaml +++ b/deploy/charts/litellm/templates/deployment.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "litellm.fullname" . }}-proxy + name: {{ include "litellm.fullname" . }} labels: {{- include "litellm.labels" . | nindent 4 }} spec: @@ -41,12 +41,12 @@ spec: - name: DATABASE_USERNAME valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials key: username - name: PGPASSWORD valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials key: password - name: DATABASE_HOST value: {{ .Release.Name }}-postgresql @@ -108,12 +108,12 @@ spec: - name: DATABASE_USERNAME valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials key: username - name: DATABASE_PASSWORD valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials key: password - name: DATABASE_HOST value: {{ .Release.Name }}-postgresql @@ -140,7 +140,7 @@ spec: - name: PROXY_MASTER_KEY valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-masterkey + name: {{ include "litellm.fullname" . }}-masterkey key: masterkey envFrom: {{- range .Values.environmentSecrets }} @@ -150,16 +150,7 @@ spec: args: - --config - /etc/litellm/config.yaml - # command: - # - bash - # - -c - # - | - # ls -la /etc/litellm/; cat /etc/litellm/config.yaml; export - # find / 2>/dev/null | grep -v -e '^/proc' -e '^/sys' -e '^/dev' >/tmp/before.list - # prisma generate - # find / 2>/dev/null | grep -v -e '^/proc' -e '^/sys' -e '^/dev' >/tmp/after.list - # diff -ruN /tmp/before.list /tmp/after.list - # sleep 3600 + - --run_gunicorn ports: - name: http containerPort: {{ .Values.service.port }} diff --git a/deploy/charts/litellm-helm/templates/hpa.yaml b/deploy/charts/litellm/templates/hpa.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/hpa.yaml rename to deploy/charts/litellm/templates/hpa.yaml diff --git a/deploy/charts/litellm-helm/templates/ingress-proxy.yaml b/deploy/charts/litellm/templates/ingress.yaml similarity index 96% rename from deploy/charts/litellm-helm/templates/ingress-proxy.yaml rename to deploy/charts/litellm/templates/ingress.yaml index 95bf83c99..09e8d715a 100644 --- a/deploy/charts/litellm-helm/templates/ingress-proxy.yaml +++ b/deploy/charts/litellm/templates/ingress.yaml @@ -1,5 +1,5 @@ {{- if .Values.ingress.enabled -}} -{{- $fullName := (printf "%s%s" (include "litellm.fullname" .) "-proxy") -}} +{{- $fullName := include "litellm.fullname" . -}} {{- $svcPort := .Values.service.port -}} {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} diff --git a/deploy/charts/litellm-helm/templates/secret-dbcredentials.yaml b/deploy/charts/litellm/templates/secret-dbcredentials.yaml similarity index 87% rename from deploy/charts/litellm-helm/templates/secret-dbcredentials.yaml rename to deploy/charts/litellm/templates/secret-dbcredentials.yaml index fc688effb..8851f5802 100644 --- a/deploy/charts/litellm-helm/templates/secret-dbcredentials.yaml +++ b/deploy/charts/litellm/templates/secret-dbcredentials.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Secret metadata: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials data: # Password for the "postgres" user postgres-password: {{ ( index .Values.postgresql.auth "postgres-password") | default "litellm" | b64enc }} diff --git a/deploy/charts/litellm-helm/templates/secret-masterkey.yaml b/deploy/charts/litellm/templates/secret-masterkey.yaml similarity index 75% rename from deploy/charts/litellm-helm/templates/secret-masterkey.yaml rename to deploy/charts/litellm/templates/secret-masterkey.yaml index 8b22b476c..57b854cc0 100644 --- a/deploy/charts/litellm-helm/templates/secret-masterkey.yaml +++ b/deploy/charts/litellm/templates/secret-masterkey.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Secret metadata: - name: {{ include "litellm.name" . }}-masterkey + name: {{ include "litellm.fullname" . }}-masterkey data: masterkey: {{ $masterkey | b64enc }} type: Opaque \ No newline at end of file diff --git a/deploy/charts/litellm-helm/templates/service-proxy.yaml b/deploy/charts/litellm/templates/service.yaml similarity index 86% rename from deploy/charts/litellm-helm/templates/service-proxy.yaml rename to deploy/charts/litellm/templates/service.yaml index 3c3c744b5..40e7f27f1 100644 --- a/deploy/charts/litellm-helm/templates/service-proxy.yaml +++ b/deploy/charts/litellm/templates/service.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "litellm.fullname" . }}-proxy + name: {{ include "litellm.fullname" . }} labels: {{- include "litellm.labels" . | nindent 4 }} spec: diff --git a/deploy/charts/litellm-helm/templates/serviceaccount.yaml b/deploy/charts/litellm/templates/serviceaccount.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/serviceaccount.yaml rename to deploy/charts/litellm/templates/serviceaccount.yaml diff --git a/deploy/charts/litellm-helm/templates/tests/test-connection.yaml b/deploy/charts/litellm/templates/tests/test-connection.yaml similarity index 92% rename from deploy/charts/litellm-helm/templates/tests/test-connection.yaml rename to deploy/charts/litellm/templates/tests/test-connection.yaml index 1f072069c..d2a4034b1 100644 --- a/deploy/charts/litellm-helm/templates/tests/test-connection.yaml +++ b/deploy/charts/litellm/templates/tests/test-connection.yaml @@ -11,5 +11,5 @@ spec: - name: wget image: busybox command: ['wget'] - args: ['{{ include "litellm.fullname" . }}:{{ .Values.service.port }}'] + args: ['{{ include "litellm.fullname" . }}:{{ .Values.service.port }}/health/readiness'] restartPolicy: Never diff --git a/deploy/charts/litellm-helm/values.yaml b/deploy/charts/litellm/values.yaml similarity index 80% rename from deploy/charts/litellm-helm/values.yaml rename to deploy/charts/litellm/values.yaml index 3c7131055..1b83fe801 100644 --- a/deploy/charts/litellm-helm/values.yaml +++ b/deploy/charts/litellm/values.yaml @@ -5,7 +5,9 @@ replicaCount: 1 image: - repository: ghcr.io/berriai/litellm + # Use "ghcr.io/berriai/litellm-database" for optimized image with database + # Alternatively, use "ghcr.io/berriai/litellm" for the default image + repository: ghcr.io/berriai/litellm-database pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. # tag: "main-latest" @@ -56,7 +58,7 @@ service: port: 8000 ingress: - enabled: true + enabled: false className: "nginx" annotations: {} # kubernetes.io/ingress.class: nginx @@ -71,6 +73,8 @@ ingress: # hosts: # - chart-example.local +# masterkey: changeit + # The elements within proxy_config are rendered as config.yaml for the proxy # Examples: https://github.com/BerriAI/litellm/tree/main/litellm/proxy/example_config_yaml # Reference: https://docs.litellm.ai/docs/proxy/configs @@ -159,61 +163,6 @@ postgresql: # A secret is created by this chart (litellm-helm) with the credentials that # the new Postgres instance should use. - existingSecret: litellm-dbcredentials - secretKeys: - userPasswordKey: password - -ui: - enabled: true - replicaCount: 1 - autoscaling: - enabled: false - image: - repository: ghcr.io/berriai/litellm-ui - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - # tag: "main-latest" - # TODO: Switch to BerryAI repo and tags if/when they provide a ui image - # https://github.com/BerriAI/litellm/pull/1505 - tag: "" - - service: - type: ClusterIP - port: 8501 - - ingress: - enabled: true - className: "nginx" - annotations: {} - hosts: - - host: ui.example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - - podAnnotations: {} - podLabels: {} - - podSecurityContext: - fsGroup: 1000 - - securityContext: - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - - resources: {} - - volumes: [] - - volumeMounts: [] - - nodeSelector: {} - - tolerations: [] - - affinity: {} \ No newline at end of file + # existingSecret: "" + # secretKeys: + # userPasswordKey: password diff --git a/docs/my-website/docs/proxy/deploy.md b/docs/my-website/docs/proxy/deploy.md index 81960a2a0..1daed99b1 100644 --- a/docs/my-website/docs/proxy/deploy.md +++ b/docs/my-website/docs/proxy/deploy.md @@ -113,6 +113,35 @@ kubectl port-forward service/litellm-service 4000:4000 Your OpenAI proxy server is now running on `http://0.0.0.0:4000`. + + + +### Step 1. Clone the repository + +```bash +git clone https://github.com/BerriAI/litellm.git +``` + +### Step 2. Deploy with Helm + +```bash +helm install \ + --set masterkey=SuPeRsEcReT \ + mydeploy \ + deploy/charts/litellm +``` + +### Step 3. Expose the service to localhost + +```bash +kubectl \ + port-forward \ + service/mydeploy-litellm \ + 8000:8000 +``` + +Your OpenAI proxy server is now running on `http://127.0.0.1:8000`. + From a2f1d2ee526b098041601fac779eef145e72cf92 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 15:44:34 -0800 Subject: [PATCH 04/65] (feat) set key-model budgets --- litellm/proxy/_types.py | 2 ++ litellm/proxy/proxy_server.py | 5 +++++ litellm/proxy/schema.prisma | 2 ++ schema.prisma | 2 ++ 4 files changed, 11 insertions(+) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 372b953e0..b791f0dd9 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -383,6 +383,8 @@ class LiteLLM_VerificationToken(LiteLLMBase): budget_reset_at: Optional[datetime] = None allowed_cache_controls: Optional[list] = [] permissions: Dict = {} + model_spend: Dict = {} + model_max_budget: Dict = {} class UserAPIKeyAuth( diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 37f55072e..6866b142a 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1635,6 +1635,7 @@ async def generate_key_helper_fn( key_alias: Optional[str] = None, allowed_cache_controls: Optional[list] = [], permissions: Optional[dict] = {}, + model_max_budget: Optional[dict] = {}, ): global prisma_client, custom_db_client, user_api_key_cache @@ -1668,6 +1669,8 @@ async def generate_key_helper_fn( config_json = json.dumps(config) permissions_json = json.dumps(permissions) metadata_json = json.dumps(metadata) + model_max_budget_json = json.dumps(model_max_budget) + user_id = user_id or str(uuid.uuid4()) user_role = user_role or "app_user" tpm_limit = tpm_limit @@ -1710,6 +1713,7 @@ async def generate_key_helper_fn( "budget_reset_at": key_reset_at, "allowed_cache_controls": allowed_cache_controls, "permissions": permissions_json, + "model_max_budget": model_max_budget_json, } if ( general_settings.get("allow_user_auth", False) == True @@ -3059,6 +3063,7 @@ async def generate_key_fn( - max_parallel_requests: Optional[int] - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x. - metadata: Optional[dict] - Metadata for key, store information for key. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" } - permissions: Optional[dict] - key-specific permissions. Currently just used for turning off pii masking (if connected). Example - {"pii": false} + - model_max_budget: Optional[dict] - key-specific model budget in USD. Example - {"text-davinci-002": 0.5, "gpt-3.5-turbo": 0.5}. IF null or {} then no model specific budget. Returns: - key: (str) The generated api key diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index 5a57b8808..df840a9ee 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -64,6 +64,8 @@ model LiteLLM_VerificationToken { budget_duration String? budget_reset_at DateTime? allowed_cache_controls String[] @default([]) + model_spend Json @default("{}") + model_max_budget Json @default("{}") } // store proxy config.yaml diff --git a/schema.prisma b/schema.prisma index 5a57b8808..df840a9ee 100644 --- a/schema.prisma +++ b/schema.prisma @@ -64,6 +64,8 @@ model LiteLLM_VerificationToken { budget_duration String? budget_reset_at DateTime? allowed_cache_controls String[] @default([]) + model_spend Json @default("{}") + model_max_budget Json @default("{}") } // store proxy config.yaml From 2c9d142e420502ccb7505032063a6cf67e4fd78b Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 16:15:24 -0800 Subject: [PATCH 05/65] (feat) track key spend per model --- litellm/proxy/proxy_server.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 6866b142a..b49890fe7 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -980,10 +980,22 @@ async def update_database( # Calculate the new cost by adding the existing cost and response_cost new_spend = existing_spend + response_cost - verbose_proxy_logger.debug(f"new cost: {new_spend}") + # track cost per model, for the given key + spend_per_model = existing_spend_obj.model_spend or {} + current_model = kwargs.get("model") + if current_model is not None and spend_per_model is not None: + if spend_per_model.get(current_model) is None: + spend_per_model[current_model] = response_cost + else: + spend_per_model[current_model] += response_cost + + verbose_proxy_logger.debug( + f"new cost: {new_spend}, new spend per model: {spend_per_model}" + ) # Update the cost column for the given token await prisma_client.update_data( - token=token, data={"spend": new_spend} + token=token, + data={"spend": new_spend, "model_spend": spend_per_model}, ) valid_token = user_api_key_cache.get_cache(key=token) From d65c6d38692979dbb4aaaa438f4fcfddee22c02e Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 16:32:17 -0800 Subject: [PATCH 06/65] (feat) track spend key-model, user-model, team-model --- litellm/proxy/proxy_server.py | 24 +++++++++++++++++++++++- litellm/proxy/schema.prisma | 4 ++++ schema.prisma | 4 ++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index b49890fe7..dda0d59c9 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -937,6 +937,16 @@ async def update_database( # Calculate the new cost by adding the existing cost and response_cost existing_spend_obj.spend = existing_spend + response_cost + # track cost per model, for the given user + spend_per_model = existing_spend_obj.model_spend or {} + current_model = kwargs.get("model") + if current_model is not None and spend_per_model is not None: + if spend_per_model.get(current_model) is None: + spend_per_model[current_model] = response_cost + else: + spend_per_model[current_model] += response_cost + existing_spend_obj.model_spend = spend_per_model + valid_token = user_api_key_cache.get_cache(key=id) if valid_token is not None and isinstance(valid_token, dict): user_api_key_cache.set_cache( @@ -1001,6 +1011,7 @@ async def update_database( valid_token = user_api_key_cache.get_cache(key=token) if valid_token is not None: valid_token.spend = new_spend + valid_token.model_spend = spend_per_model user_api_key_cache.set_cache(key=token, value=valid_token) elif custom_db_client is not None: # Fetch the existing cost for the given token @@ -1080,10 +1091,21 @@ async def update_database( # Calculate the new cost by adding the existing cost and response_cost new_spend = existing_spend + response_cost + # track cost per model, for the given team + spend_per_model = existing_spend_obj.model_spend or {} + current_model = kwargs.get("model") + if current_model is not None and spend_per_model is not None: + if spend_per_model.get(current_model) is None: + spend_per_model[current_model] = response_cost + else: + spend_per_model[current_model] += response_cost + verbose_proxy_logger.debug(f"new cost: {new_spend}") # Update the cost column for the given token await prisma_client.update_data( - team_id=team_id, data={"spend": new_spend}, table_name="team" + team_id=team_id, + data={"spend": new_spend, "model_spend": spend_per_model}, + table_name="team", ) elif custom_db_client is not None: diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index df840a9ee..101cf9b7f 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -24,6 +24,8 @@ model LiteLLM_TeamTable { budget_reset_at DateTime? created_at DateTime @default(now()) @map("created_at") updated_at DateTime @default(now()) @updatedAt @map("updated_at") + model_spend Json @default("{}") + model_max_budget Json @default("{}") } // Track spend, rate limit, budget Users @@ -41,6 +43,8 @@ model LiteLLM_UserTable { budget_duration String? budget_reset_at DateTime? allowed_cache_controls String[] @default([]) + model_spend Json @default("{}") + model_max_budget Json @default("{}") } // Generate Tokens for Proxy diff --git a/schema.prisma b/schema.prisma index df840a9ee..101cf9b7f 100644 --- a/schema.prisma +++ b/schema.prisma @@ -24,6 +24,8 @@ model LiteLLM_TeamTable { budget_reset_at DateTime? created_at DateTime @default(now()) @map("created_at") updated_at DateTime @default(now()) @updatedAt @map("updated_at") + model_spend Json @default("{}") + model_max_budget Json @default("{}") } // Track spend, rate limit, budget Users @@ -41,6 +43,8 @@ model LiteLLM_UserTable { budget_duration String? budget_reset_at DateTime? allowed_cache_controls String[] @default([]) + model_spend Json @default("{}") + model_max_budget Json @default("{}") } // Generate Tokens for Proxy From e8dcf8fa130c6af87922b7c8b9911e5e0868d66e Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 17:00:23 -0800 Subject: [PATCH 07/65] (fix) setting model_max_budget --- litellm/proxy/proxy_server.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index dda0d59c9..7d30c959f 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1763,6 +1763,11 @@ async def generate_key_helper_fn( saved_token["metadata"] = json.loads(saved_token["metadata"]) if isinstance(saved_token["permissions"], str): saved_token["permissions"] = json.loads(saved_token["permissions"]) + if isinstance(saved_token["model_max_budget"], str): + saved_token["model_max_budget"] = json.loads( + saved_token["model_max_budget"] + ) + if saved_token.get("expires", None) is not None and isinstance( saved_token["expires"], datetime ): From 4ea354ee6ee5bc03a8c9145247c9fda86f97891b Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 17:19:01 -0800 Subject: [PATCH 08/65] (feat) budgets per model --- litellm/proxy/proxy_server.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 7d30c959f..8de8cd9ad 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -376,6 +376,11 @@ async def user_api_key_auth( # 3. If 'user' passed to /chat/completions, /embeddings endpoint is in budget # 4. If token is expired # 5. If token spend is under Budget for the token + # 6. If token spend per model is under budget per model + + request_data = await _read_request_body( + request=request + ) # request data, used across all checks. Making this easily available # Check 1. If token can call model litellm.model_alias_map = valid_token.aliases @@ -450,7 +455,6 @@ async def user_api_key_auth( if ( litellm.max_user_budget is not None ): # Check if 'user' passed in /chat/completions is in budget, only checked if litellm.max_user_budget is set - request_data = await _read_request_body(request=request) user_passed_to_chat_completions = request_data.get("user", None) if user_passed_to_chat_completions is not None: user_id_list.append(user_passed_to_chat_completions) @@ -587,6 +591,25 @@ async def user_api_key_auth( f"ExceededTokenBudget: Current spend for token: {valid_token.spend}; Max Budget for Token: {valid_token.max_budget}" ) + # Check 5. Token Model Spend is under Model budget + max_budget_per_model = valid_token.model_max_budget + spend_per_model = valid_token.model_spend + + if max_budget_per_model is not None and spend_per_model is not None: + current_model = request_data.get("model") + if current_model is not None: + current_model_spend = spend_per_model.get(current_model, None) + current_model_budget = max_budget_per_model.get(current_model, None) + + if ( + current_model_spend is not None + and current_model_budget is not None + ): + if current_model_spend > current_model_budget: + raise Exception( + f"ExceededModelBudget: Current spend for model: {current_model_spend}; Max Budget for Model: {current_model_budget}" + ) + # Token passed all checks api_key = valid_token.token From 2e074a8585e7791b7ce66da0d17475f4e55646e6 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 17:45:49 -0800 Subject: [PATCH 09/65] (test) test_call_with_key_over_model_budget --- litellm/tests/test_key_generate_prisma.py | 110 ++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/litellm/tests/test_key_generate_prisma.py b/litellm/tests/test_key_generate_prisma.py index 89d4f4d3e..1f5507d3f 100644 --- a/litellm/tests/test_key_generate_prisma.py +++ b/litellm/tests/test_key_generate_prisma.py @@ -1101,6 +1101,116 @@ def test_call_with_key_over_budget(prisma_client): print(vars(e)) +def test_call_with_key_over_model_budget(prisma_client): + # 12. Make a call with a key over budget, expect to fail + setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) + setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") + try: + + async def test(): + await litellm.proxy.proxy_server.prisma_client.connect() + + # set budget for chatgpt-v-2 to 0.000001, expect the next request to fail + request = GenerateKeyRequest( + max_budget=1000, + model_max_budget={ + "chatgpt-v-2": 0.000001, + }, + metadata={"user_api_key": 0.0001}, + ) + key = await generate_key_fn(request) + print(key) + + generated_key = key.key + user_id = key.user_id + bearer_token = "Bearer " + generated_key + + request = Request(scope={"type": "http"}) + request._url = URL(url="/chat/completions") + + async def return_body(): + return b'{"model": "chatgpt-v-2"}' + + request.body = return_body + + # use generated key to auth in + result = await user_api_key_auth(request=request, api_key=bearer_token) + print("result from user auth with new key", result) + + # update spend using track_cost callback, make 2nd request, it should fail + from litellm.proxy.proxy_server import ( + _PROXY_track_cost_callback as track_cost_callback, + ) + from litellm import ModelResponse, Choices, Message, Usage + from litellm.caching import Cache + + litellm.cache = Cache() + import time + + request_id = f"chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac{time.time()}" + + resp = ModelResponse( + id=request_id, + choices=[ + Choices( + finish_reason=None, + index=0, + message=Message( + content=" Sure! Here is a short poem about the sky:\n\nA canvas of blue, a", + role="assistant", + ), + ) + ], + model="gpt-35-turbo", # azure always has model written like this + usage=Usage(prompt_tokens=210, completion_tokens=200, total_tokens=410), + ) + await track_cost_callback( + kwargs={ + "model": "chatgpt-v-2", + "stream": False, + "litellm_params": { + "metadata": { + "user_api_key": hash_token(generated_key), + "user_api_key_user_id": user_id, + } + }, + "response_cost": 0.00002, + }, + completion_response=resp, + start_time=datetime.now(), + end_time=datetime.now(), + ) + await asyncio.sleep(10) + # test spend_log was written and we can read it + spend_logs = await view_spend_logs(request_id=request_id) + + print("read spend logs", spend_logs) + assert len(spend_logs) == 1 + + spend_log = spend_logs[0] + + assert spend_log.request_id == request_id + assert spend_log.spend == float("2e-05") + assert spend_log.model == "chatgpt-v-2" + assert ( + spend_log.cache_key + == "a61ae14fe4a8b8014a61e6ae01a100c8bc6770ac37c293242afed954bc69207d" + ) + + # use generated key to auth in + result = await user_api_key_auth(request=request, api_key=bearer_token) + print("result from user auth with new key", result) + pytest.fail(f"This should have failed!. They key crossed it's budget") + + asyncio.run(test()) + except Exception as e: + # print(f"Error - {str(e)}") + traceback.print_exc() + error_detail = e.message + assert "Authentication Error, ExceededModelBudget:" in error_detail + print(vars(e)) + + @pytest.mark.asyncio() async def test_call_with_key_never_over_budget(prisma_client): # Make a call with a key with budget=None, it should never fail From 659a394a36803932a13e735c084c89c4c3b82c28 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 17:46:34 -0800 Subject: [PATCH 10/65] (fix) types for model_max_budget --- litellm/proxy/_types.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index b791f0dd9..be2cdd6ef 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -155,6 +155,9 @@ class GenerateKeyRequest(GenerateRequestBase): aliases: Optional[dict] = {} config: Optional[dict] = {} permissions: Optional[dict] = {} + model_max_budget: Optional[dict] = ( + {} + ) # {"gpt-4": 5.0, "gpt-3.5-turbo": 5.0}, defaults to {} class GenerateKeyResponse(GenerateKeyRequest): @@ -167,7 +170,13 @@ class GenerateKeyResponse(GenerateKeyRequest): def set_model_info(cls, values): if values.get("token") is not None: values.update({"key": values.get("token")}) - dict_fields = ["metadata", "aliases", "config", "permissions"] + dict_fields = [ + "metadata", + "aliases", + "config", + "permissions", + "model_max_budget", + ] for field in dict_fields: value = values.get(field) if value is not None and isinstance(value, str): From e76a3c5ce5f5729d4b06de710036fdf4b91f5f53 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 17:47:22 -0800 Subject: [PATCH 11/65] (fix) _types for model_max_budget --- litellm/proxy/_types.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index b791f0dd9..be2cdd6ef 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -155,6 +155,9 @@ class GenerateKeyRequest(GenerateRequestBase): aliases: Optional[dict] = {} config: Optional[dict] = {} permissions: Optional[dict] = {} + model_max_budget: Optional[dict] = ( + {} + ) # {"gpt-4": 5.0, "gpt-3.5-turbo": 5.0}, defaults to {} class GenerateKeyResponse(GenerateKeyRequest): @@ -167,7 +170,13 @@ class GenerateKeyResponse(GenerateKeyRequest): def set_model_info(cls, values): if values.get("token") is not None: values.update({"key": values.get("token")}) - dict_fields = ["metadata", "aliases", "config", "permissions"] + dict_fields = [ + "metadata", + "aliases", + "config", + "permissions", + "model_max_budget", + ] for field in dict_fields: value = values.get(field) if value is not None and isinstance(value, str): From 62d0c54cfbc5749544c0d6ef08ac164d95b4893f Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 18:18:35 -0800 Subject: [PATCH 12/65] (fix) issue with storing model max budget --- litellm/proxy/_types.py | 2 ++ litellm/proxy/proxy_server.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index be2cdd6ef..5d74b9ded 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -421,6 +421,8 @@ class LiteLLM_UserTable(LiteLLMBase): user_id: str max_budget: Optional[float] spend: float = 0.0 + model_max_budget: Optional[Dict] = {} + model_spend: Optional[Dict] = {} user_email: Optional[str] models: list = [] diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 7d30c959f..72bb3b25a 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -940,6 +940,7 @@ async def update_database( # track cost per model, for the given user spend_per_model = existing_spend_obj.model_spend or {} current_model = kwargs.get("model") + if current_model is not None and spend_per_model is not None: if spend_per_model.get(current_model) is None: spend_per_model[current_model] = response_cost @@ -953,7 +954,9 @@ async def update_database( key=id, value=existing_spend_obj.json() ) - verbose_proxy_logger.debug(f"new cost: {existing_spend_obj.spend}") + verbose_proxy_logger.debug( + f"user - new cost: {existing_spend_obj.spend}, user_id: {id}" + ) data_list.append(existing_spend_obj) # Update the cost column for the given user id From 448537e6846473e0d01c847761c101c9e3b203fd Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 11:04:56 -0800 Subject: [PATCH 13/65] feat(presidio_pii_masking.py): allow request level controls for turning on/off pii masking https://github.com/BerriAI/litellm/issues/2003 --- docs/my-website/docs/proxy/pii_masking.md | 75 +++++++++++++++++++++ litellm/proxy/hooks/presidio_pii_masking.py | 57 ++++++++++++++-- litellm/proxy/proxy_server.py | 13 ++++ 3 files changed, 141 insertions(+), 4 deletions(-) diff --git a/docs/my-website/docs/proxy/pii_masking.md b/docs/my-website/docs/proxy/pii_masking.md index 348ce207d..dafb5876c 100644 --- a/docs/my-website/docs/proxy/pii_masking.md +++ b/docs/my-website/docs/proxy/pii_masking.md @@ -72,3 +72,78 @@ curl --location 'http://0.0.0.0:8000/key/generate' \ ``` +## Turn on/off per request + +The proxy support 2 request-level PII controls: + +- *no-pii*: Optional(bool) - Allow user to turn off pii masking per request. +- *output_parse_pii*: Optional(bool) - Allow user to turn off pii output parsing per request. + +### Usage + +**Step 1. Create key with pii permissions** + +Set `allow_pii_controls` to true for a given key. This will allow the user to set request-level PII controls. + +```bash +curl --location 'http://0.0.0.0:8000/key/generate' \ +--header 'Authorization: Bearer my-master-key' \ +--header 'Content-Type: application/json' \ +--data '{ + "permissions": {"allow_pii_controls": true} +}' +``` + +**Step 2. Turn off pii output parsing** + +```python +import os +from openai import OpenAI + +client = OpenAI( + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), + base_url="http://0.0.0.0:8000" +) + +chat_completion = client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "My name is Jane Doe, my number is 8382043839", + } + ], + model="gpt-3.5-turbo", + extra_body={ + "content_safety": {"output_parse_pii": False} + } +) +``` + +**Step 3: See response** + +``` +{ + "id": "chatcmpl-8c5qbGTILZa1S4CK3b31yj5N40hFN", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hi [PERSON], what can I help you with?", + "role": "assistant" + } + } + ], + "created": 1704089632, + "model": "gpt-35-turbo", + "object": "chat.completion", + "system_fingerprint": null, + "usage": { + "completion_tokens": 47, + "prompt_tokens": 12, + "total_tokens": 59 + }, + "_response_ms": 1753.426 +} +``` \ No newline at end of file diff --git a/litellm/proxy/hooks/presidio_pii_masking.py b/litellm/proxy/hooks/presidio_pii_masking.py index 6ea329613..031f4e2d4 100644 --- a/litellm/proxy/hooks/presidio_pii_masking.py +++ b/litellm/proxy/hooks/presidio_pii_masking.py @@ -119,6 +119,9 @@ class _OPTIONAL_PresidioPIIMasking(CustomLogger): call_type: str, ): """ + - Check if request turned off pii + - Check if user allowed to turn off pii (key permissions -> 'allow_pii_controls') + - Take the request data - Call /analyze -> get the results - Call /anonymize w/ the analyze results -> get the redacted text @@ -126,13 +129,59 @@ class _OPTIONAL_PresidioPIIMasking(CustomLogger): For multiple messages in /chat/completions, we'll need to call them in parallel. """ permissions = user_api_key_dict.permissions - - if permissions.get("pii", True) == False: # allow key to turn off pii masking - return data - output_parse_pii = permissions.get( "output_parse_pii", litellm.output_parse_pii ) # allow key to turn on/off output parsing for pii + no_pii = permissions.get( + "no-pii", None + ) # allow key to turn on/off pii masking (if user is allowed to set pii controls, then they can override the key defaults) + + if no_pii is None: + # check older way of turning on/off pii + no_pii = not permissions.get("pii", True) + + content_safety = data.get("content_safety", None) + verbose_proxy_logger.debug(f"content_safety: {content_safety}") + ## Request-level turn on/off PII controls ## + if content_safety is not None and isinstance(content_safety, dict): + # pii masking ## + if ( + content_safety.get("no-pii", None) is not None + and content_safety.get("no-pii") == True + ): + # check if user allowed to turn this off + if permissions.get("allow_pii_controls", False) == False: + raise HTTPException( + status_code=400, + detail={"error": "Not allowed to set PII controls per request"}, + ) + else: # user allowed to turn off pii masking + no_pii = content_safety.get("no-pii") + if not isinstance(no_pii, bool): + raise HTTPException( + status_code=400, + detail={"error": "no_pii needs to be a boolean value"}, + ) + ## pii output parsing ## + if content_safety.get("output_parse_pii", None) is not None: + # check if user allowed to turn this off + if permissions.get("allow_pii_controls", False) == False: + raise HTTPException( + status_code=400, + detail={"error": "Not allowed to set PII controls per request"}, + ) + else: # user allowed to turn on/off pii output parsing + output_parse_pii = content_safety.get("output_parse_pii") + if not isinstance(output_parse_pii, bool): + raise HTTPException( + status_code=400, + detail={ + "error": "output_parse_pii needs to be a boolean value" + }, + ) + + if no_pii == False: # turn off pii masking + return data if call_type == "completion": # /chat/completions requests messages = data["messages"] diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index b5349fffb..ebf9bccb4 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -3079,6 +3079,19 @@ async def generate_key_fn( - metadata: Optional[dict] - Metadata for key, store information for key. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" } - permissions: Optional[dict] - key-specific permissions. Currently just used for turning off pii masking (if connected). Example - {"pii": false} + Examples: + + 1. Allow users to turn on/off pii masking + + ```bash + curl --location 'http://0.0.0.0:8000/key/generate' \ + --header 'Authorization: Bearer sk-1234' \ + --header 'Content-Type: application/json' \ + --data '{ + "permissions": {"allow_pii_controls": true} + }' + ``` + Returns: - key: (str) The generated api key - expires: (datetime) Datetime object for when key expires. From 844df2412ee70b4f46e052fa4b0bafab6514df09 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 13:03:24 -0800 Subject: [PATCH 14/65] (feat) set fast api root path --- litellm/proxy/proxy_server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index b5349fffb..a02042ab5 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -143,6 +143,9 @@ app = FastAPI( title="LiteLLM API", description=f"Proxy Server to call 100+ LLMs in the OpenAI format\n\n{ui_message}", version=version, + root_path=os.environ.get( + "SERVER_ROOT_PATH", "" + ), # check if user passed root path, FastAPI defaults this value to "" ) From 67932dccbc9190bccc2d3352d10a6418403beb57 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 13:12:06 -0800 Subject: [PATCH 15/65] (docs) setting server root path --- docs/my-website/docs/proxy/deploy.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/my-website/docs/proxy/deploy.md b/docs/my-website/docs/proxy/deploy.md index c3a84f1ea..091e10627 100644 --- a/docs/my-website/docs/proxy/deploy.md +++ b/docs/my-website/docs/proxy/deploy.md @@ -154,7 +154,22 @@ Your OpenAI proxy server is now running on `http://0.0.0.0:4000`. -## Setting SSL Certification +## Advanced Deployment Settings + +### Customization of the server root path + +:::info + +In a Kubernetes deployment, it's possible to utilize a shared DNS to host multiple applications by modifying the virtual service + +::: + +Customize the root path to eliminate the need for employing multiple DNS configurations during deployment. + +👉 Set `SERVER_ROOT_PATH` in your .env and this will be set as your server root path + + +### Setting SSL Certification Use this, If you need to set ssl certificates for your on prem litellm proxy From c5891c3ee88a76bdb51fc39b7027ddc0cdaaa409 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 13:26:45 -0800 Subject: [PATCH 16/65] (fix) docs - incorrect title --- docs/my-website/sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index 83b409969..3badfc53a 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -129,7 +129,7 @@ const sidebars = { "proxy/caching", { "type": "category", - "label": "Logging, Alerting, Caching", + "label": "Logging, Alerting", "items": [ "proxy/logging", "proxy/alerting", From 46b11324dd3ec7f6e2b2d6f1a3eb3b3c12848e70 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 13:57:42 -0800 Subject: [PATCH 17/65] (fix) ui - fix method not alowed error --- ui/litellm-dashboard/src/components/networking.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 7905ed7db..5874ae2a9 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -239,7 +239,7 @@ export const userSpendLogsCall = async ( export const keyInfoCall = async (accessToken: String, keys: String[]) => { try { - let url = proxyBaseUrl ? `${proxyBaseUrl}/v2/key/info` : `/key/info`; + let url = proxyBaseUrl ? `${proxyBaseUrl}/v2/key/info` : `/v2/key/info`; const response = await fetch(url, { method: "POST", From c069403b050ed04f310da3194196b23569a18f7c Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 14:06:52 -0800 Subject: [PATCH 18/65] (fix) ui build --- litellm/proxy/_experimental/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/app/page-40ca7f9639d3a19d.js | 1 + .../out/_next/static/chunks/app/page-7bb820bd6902dbf2.js | 1 - litellm/proxy/_experimental/out/index.html | 2 +- litellm/proxy/_experimental/out/index.txt | 4 ++-- ui/litellm-dashboard/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/app/page-40ca7f9639d3a19d.js | 1 + .../out/_next/static/chunks/app/page-7bb820bd6902dbf2.js | 1 - ui/litellm-dashboard/out/index.html | 2 +- ui/litellm-dashboard/out/index.txt | 4 ++-- 14 files changed, 10 insertions(+), 10 deletions(-) rename litellm/proxy/_experimental/out/_next/static/{unBuvDqydg0yodtP5c3nQ => -A8U_xwfNq_YFjqXwnPm2}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{unBuvDqydg0yodtP5c3nQ => -A8U_xwfNq_YFjqXwnPm2}/_ssgManifest.js (100%) create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js rename ui/litellm-dashboard/out/_next/static/{unBuvDqydg0yodtP5c3nQ => -A8U_xwfNq_YFjqXwnPm2}/_buildManifest.js (100%) rename ui/litellm-dashboard/out/_next/static/{unBuvDqydg0yodtP5c3nQ => -A8U_xwfNq_YFjqXwnPm2}/_ssgManifest.js (100%) create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html index 03563194a..66998fa5b 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/unBuvDqydg0yodtP5c3nQ/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/unBuvDqydg0yodtP5c3nQ/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/unBuvDqydg0yodtP5c3nQ/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/unBuvDqydg0yodtP5c3nQ/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js new file mode 100644 index 000000000..cf687a562 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/app/page-7bb820bd6902dbf2.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js deleted file mode 100644 index 5402c8d1f..000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/index.html b/litellm/proxy/_experimental/out/index.html index 80296f4f6..775685e97 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 b6bb2c868..7751ad7e7 100644 --- a/litellm/proxy/_experimental/out/index.txt +++ b/litellm/proxy/_experimental/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-7bb820bd6902dbf2.js"],""] +3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-40ca7f9639d3a19d.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["unBuvDqydg0yodtP5c3nQ",[[["",{"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/c18941d97fb7245b.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["-A8U_xwfNq_YFjqXwnPm2",[[["",{"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/c18941d97fb7245b.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/out/404.html b/ui/litellm-dashboard/out/404.html index 03563194a..66998fa5b 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/unBuvDqydg0yodtP5c3nQ/_buildManifest.js b/ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/unBuvDqydg0yodtP5c3nQ/_buildManifest.js rename to ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/unBuvDqydg0yodtP5c3nQ/_ssgManifest.js b/ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/unBuvDqydg0yodtP5c3nQ/_ssgManifest.js rename to ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js new file mode 100644 index 000000000..cf687a562 --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/app/page-7bb820bd6902dbf2.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js deleted file mode 100644 index 5402c8d1f..000000000 --- a/ui/litellm-dashboard/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/index.html b/ui/litellm-dashboard/out/index.html index 80296f4f6..775685e97 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 b6bb2c868..7751ad7e7 100644 --- a/ui/litellm-dashboard/out/index.txt +++ b/ui/litellm-dashboard/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-7bb820bd6902dbf2.js"],""] +3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-40ca7f9639d3a19d.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["unBuvDqydg0yodtP5c3nQ",[[["",{"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/c18941d97fb7245b.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["-A8U_xwfNq_YFjqXwnPm2",[[["",{"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/c18941d97fb7245b.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 From c403ebe61351e1e6512fbf760f2925f0a14a770e Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 14:10:27 -0800 Subject: [PATCH 19/65] =?UTF-8?q?bump:=20version=201.25.0=20=E2=86=92=201.?= =?UTF-8?q?25.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 98446b79b..49160f4a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.25.0" +version = "1.25.1" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -74,7 +74,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.25.0" +version = "1.25.1" version_files = [ "pyproject.toml:^version" ] From b10bf2083cdfd03832982ea88be5e120d68ab45d Mon Sep 17 00:00:00 2001 From: Andres Barbaro Date: Sat, 17 Feb 2024 17:31:37 -0600 Subject: [PATCH 20/65] Update gemini documentation --- docs/my-website/docs/providers/gemini.md | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/my-website/docs/providers/gemini.md b/docs/my-website/docs/providers/gemini.md index 9d5eb298b..44d744866 100644 --- a/docs/my-website/docs/providers/gemini.md +++ b/docs/my-website/docs/providers/gemini.md @@ -16,6 +16,34 @@ response = completion( ) ``` +## Specifying Safety Settings +In certain use-cases you may need to make calls to the models and pass [safety settigns](https://ai.google.dev/docs/safety_setting_gemini) different from the defaults. To do so, simple pass the `safety_settings` argument to `completion` or `acompletion`. For example: + +```python +response = completion( + model="gemini/gemini-pro", + messages=[{"role": "user", "content": "write code for saying hi from LiteLLM"}] + safety_settings=[ + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "BLOCK_NONE", + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "threshold": "BLOCK_NONE", + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "threshold": "BLOCK_NONE", + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "threshold": "BLOCK_NONE", + }, + ] +) +``` + # Gemini-Pro-Vision LiteLLM Supports the following image types passed in `url` - Images with direct links - https://storage.googleapis.com/github-repo/img/gemini/intro/landmark3.jpg From 1652e894c1cac5ce655fa2cc600479c6dbedcf2e Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 15:34:55 -0800 Subject: [PATCH 21/65] (docs) set budget per model --- docs/my-website/docs/proxy/virtual_keys.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/my-website/docs/proxy/virtual_keys.md b/docs/my-website/docs/proxy/virtual_keys.md index 2be4b95c1..83994701c 100644 --- a/docs/my-website/docs/proxy/virtual_keys.md +++ b/docs/my-website/docs/proxy/virtual_keys.md @@ -93,6 +93,7 @@ Request Params: - `config`: *Optional[dict]* - any key-specific configs, overrides config in config.yaml - `spend`: *Optional[int]* - Amount spent by key. Default is 0. Will be updated by proxy whenever key is used. https://docs.litellm.ai/docs/proxy/virtual_keys#managing-auth---tracking-spend - `max_budget`: *Optional[float]* - Specify max budget for a given key. +- `model_max_budget`: *Optional[dict[str, float]]* - Specify max budget for each model, `model_max_budget={"gpt4": 0.5, "gpt-5": 0.01}` - `max_parallel_requests`: *Optional[int]* - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x. - `metadata`: *Optional[dict]* - Metadata for key, store information for key. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" } @@ -676,8 +677,6 @@ general_settings: ### [BETA] Dynamo DB -Only live in `v1.16.21.dev1`. - #### Step 1. Save keys to env ```shell From 44202aa25b6cead54665306111b9dc6685c8b6ae Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 16:00:08 -0800 Subject: [PATCH 22/65] (fix) dynamo db test - new model_spend params --- litellm/proxy/db/dynamo_db.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/litellm/proxy/db/dynamo_db.py b/litellm/proxy/db/dynamo_db.py index 206fee777..08b365191 100644 --- a/litellm/proxy/db/dynamo_db.py +++ b/litellm/proxy/db/dynamo_db.py @@ -287,6 +287,8 @@ class DynamoDBWrapper(CustomDB): or k == "config" or k == "metadata" or k == "permissions" + or k == "model_spend" + or k == "model_max_budget" ) and v is not None and isinstance(v, str) From 2de5a60993d9fd5986940b42f409243a73898734 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Sat, 17 Feb 2024 16:45:46 -0800 Subject: [PATCH 23/65] Update README.md --- enterprise/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enterprise/README.md b/enterprise/README.md index 483ee1e44..12a271517 100644 --- a/enterprise/README.md +++ b/enterprise/README.md @@ -6,9 +6,9 @@ Code in this folder is licensed under a commercial license. Please review the [L 👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat?month=2024-02) -## Enterprise Features: +## Enterprise Features: [Docs](https://docs.litellm.ai/docs/proxy/enterprise) -- Track, View spend per tag https://docs.litellm.ai/docs/proxy/spend +- Track, View spend for custom tags - Custom API / microservice callbacks - Google Text Moderation API From a3d197fa1fb42e047d83d1a5a7af50caffa1e12c Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Sat, 17 Feb 2024 16:46:11 -0800 Subject: [PATCH 24/65] Update README.md --- enterprise/README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/enterprise/README.md b/enterprise/README.md index 12a271517..d5c27bab6 100644 --- a/enterprise/README.md +++ b/enterprise/README.md @@ -6,9 +6,4 @@ Code in this folder is licensed under a commercial license. Please review the [L 👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat?month=2024-02) -## Enterprise Features: [Docs](https://docs.litellm.ai/docs/proxy/enterprise) - -- Track, View spend for custom tags -- Custom API / microservice callbacks -- Google Text Moderation API - +See all Enterprise Features here 👉 [Docs](https://docs.litellm.ai/docs/proxy/enterprise) From 73acdf373608bd5fb766c492b4533d5a9c729703 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 17:52:32 -0800 Subject: [PATCH 25/65] refactor: rename enterprise specific hooks and callbacks to be more precise --- .../example_logging_api.py | 0 .../generic_api_callback.py | 0 .../google_text_moderation.py | 0 .../{hooks => enterprise_hooks}/llama_guard.py | 0 litellm/proxy/proxy_server.py | 2 +- litellm/utils.py | 14 +++++--------- 6 files changed, 6 insertions(+), 10 deletions(-) rename enterprise/{callbacks => enterprise_callbacks}/example_logging_api.py (100%) rename enterprise/{callbacks => enterprise_callbacks}/generic_api_callback.py (100%) rename enterprise/{hooks => enterprise_hooks}/google_text_moderation.py (100%) rename enterprise/{hooks => enterprise_hooks}/llama_guard.py (100%) diff --git a/enterprise/callbacks/example_logging_api.py b/enterprise/enterprise_callbacks/example_logging_api.py similarity index 100% rename from enterprise/callbacks/example_logging_api.py rename to enterprise/enterprise_callbacks/example_logging_api.py diff --git a/enterprise/callbacks/generic_api_callback.py b/enterprise/enterprise_callbacks/generic_api_callback.py similarity index 100% rename from enterprise/callbacks/generic_api_callback.py rename to enterprise/enterprise_callbacks/generic_api_callback.py diff --git a/enterprise/hooks/google_text_moderation.py b/enterprise/enterprise_hooks/google_text_moderation.py similarity index 100% rename from enterprise/hooks/google_text_moderation.py rename to enterprise/enterprise_hooks/google_text_moderation.py diff --git a/enterprise/hooks/llama_guard.py b/enterprise/enterprise_hooks/llama_guard.py similarity index 100% rename from enterprise/hooks/llama_guard.py rename to enterprise/enterprise_hooks/llama_guard.py diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index bd30a621f..0f4f3d5ef 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1421,7 +1421,7 @@ class ProxyConfig: isinstance(callback, str) and callback == "llamaguard_moderations" ): - from litellm.proxy.enterprise.hooks.llama_guard import ( + from litellm.proxy.enterprise.enterprise_hooks.llama_guard import ( _ENTERPRISE_LlamaGuard, ) diff --git a/litellm/utils.py b/litellm/utils.py index d173b0092..faa464448 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -34,6 +34,7 @@ from dataclasses import ( try: # this works in python 3.8 import pkg_resources + filename = pkg_resources.resource_filename(__name__, "llms/tokenizers") # try: # filename = str( @@ -42,6 +43,7 @@ try: except: # this works in python 3.9+ from importlib import resources + filename = str( resources.files(litellm).joinpath("llms/tokenizers") # for python 3.10 ) # for python 3.10+ @@ -87,16 +89,10 @@ from .exceptions import ( UnprocessableEntityError, ) -# Import Enterprise features -project_path = abspath(join(dirname(__file__), "..", "..")) -# Add the "enterprise" directory to sys.path -verbose_logger.debug(f"current project_path: {project_path}") -enterprise_path = abspath(join(project_path, "enterprise")) -sys.path.append(enterprise_path) - -verbose_logger.debug(f"sys.path: {sys.path}") try: - from enterprise.callbacks.generic_api_callback import GenericAPILogger + from .proxy.enterprise.enterprise_callbacks.generic_api_callback import ( + GenericAPILogger, + ) except Exception as e: verbose_logger.debug(f"Exception import enterprise features {str(e)}") From 901a1a3655ba316ceca37bcc64ca2fea216ac51f Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 18:41:37 -0800 Subject: [PATCH 26/65] (fix) litellm bug in --- litellm/proxy/proxy_server.py | 32 ++++++++++++++++++-------------- litellm/proxy/utils.py | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index bd30a621f..3da7f36f7 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -93,6 +93,7 @@ from litellm.proxy.utils import ( html_form, _read_request_body, _is_valid_team_configs, + _is_user_proxy_admin, ) from litellm.proxy.secret_managers.google_kms import load_google_kms import pydantic @@ -499,11 +500,7 @@ async def user_api_key_auth( continue assert isinstance(_user, dict) # check if user is admin # - if ( - _user.get("user_role", None) is not None - and _user.get("user_role") == "proxy_admin" - ): - return UserAPIKeyAuth(api_key=master_key) + # Token exists, not expired now check if its in budget for the user user_max_budget = _user.get("max_budget", None) user_current_spend = _user.get("spend", None) @@ -619,11 +616,15 @@ async def user_api_key_auth( ) ) if ( - route.startswith("/key/") - or route.startswith("/user/") - or route.startswith("/model/") - or route.startswith("/spend/") - ) and (not is_master_key_valid): + ( + route.startswith("/key/") + or route.startswith("/user/") + or route.startswith("/model/") + or route.startswith("/spend/") + ) + and (not is_master_key_valid) + and (not _is_user_proxy_admin(user_id_information)) + ): allow_user_auth = False if ( general_settings.get("allow_user_auth", False) == True @@ -715,9 +716,12 @@ async def user_api_key_auth( # Do something if the current route starts with any of the allowed routes pass else: - raise Exception( - f"This key is made for LiteLLM UI, Tried to access route: {route}. Not allowed" - ) + if _is_user_proxy_admin(user_id_information): + pass + else: + raise Exception( + f"This key is made for LiteLLM UI, Tried to access route: {route}. Not allowed" + ) return UserAPIKeyAuth(api_key=api_key, **valid_token_dict) except Exception as e: # verbose_proxy_logger.debug(f"An exception occurred - {traceback.format_exc()}") @@ -4921,7 +4925,7 @@ async def auth_callback(request: Request): if user_id is None: user_id = getattr(result, "first_name", "") + getattr(result, "last_name", "") response = await generate_key_helper_fn( - **{"duration": "1hr", "key_max_budget": 0, "models": [], "aliases": {}, "config": {}, "spend": 0, "user_id": user_id, "team_id": "litellm-dashboard", "user_email": user_email} # type: ignore + **{"duration": "1hr", "key_max_budget": 0.01, "models": [], "aliases": {}, "config": {}, "spend": 0, "user_id": user_id, "team_id": "litellm-dashboard", "user_email": user_email} # type: ignore ) key = response["token"] # type: ignore user_id = response["user_id"] # type: ignore diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 7cc0f59f1..51e7c3c15 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1408,6 +1408,22 @@ def _is_valid_team_configs(team_id=None, team_config=None, request_data=None): return +def _is_user_proxy_admin(user_id_information=None): + if ( + user_id_information == None + or len(user_id_information) == 0 + or user_id_information[0] == None + ): + return False + _user = user_id_information[0] + if ( + _user.get("user_role", None) is not None + and _user.get("user_role") == "proxy_admin" + ): + return True + return False + + # LiteLLM Admin UI - Non SSO Login html_form = """ From fbddf0bb43d75a3eb42624dc8584ead5aab5774d Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 19:03:22 -0800 Subject: [PATCH 27/65] (fix) ui - reload key spend info --- .../src/components/user_dashboard.tsx | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ui/litellm-dashboard/src/components/user_dashboard.tsx b/ui/litellm-dashboard/src/components/user_dashboard.tsx index bf95d09f7..f87303a12 100644 --- a/ui/litellm-dashboard/src/components/user_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/user_dashboard.tsx @@ -48,6 +48,11 @@ const UserDashboard: React.FC = ({ const token = searchParams.get("token"); const [accessToken, setAccessToken] = useState(null); const [userModels, setUserModels] = useState([]); + window.addEventListener('beforeunload', function() { + // Clear session storage + sessionStorage.clear(); + }); + function formatUserRole(userRole: string) { if (!userRole) { @@ -70,6 +75,7 @@ const UserDashboard: React.FC = ({ // Moved useEffect inside the component and used a condition to run fetch only if the params are available useEffect(() => { + if (token) { const decoded = jwtDecode(token) as { [key: string]: any }; if (decoded) { @@ -97,22 +103,22 @@ const UserDashboard: React.FC = ({ } } if (userID && accessToken && userRole && !data) { - const cachedData = localStorage.getItem("userData" + userID); - const cachedSpendData = localStorage.getItem("userSpendData" + userID); - const cachedUserModels = localStorage.getItem("userModels" + userID); + const cachedData = sessionStorage.getItem("userData" + userID); + const cachedSpendData = sessionStorage.getItem("userSpendData" + userID); + const cachedUserModels = sessionStorage.getItem("userModels" + userID); if (cachedData && cachedSpendData && cachedUserModels) { setData(JSON.parse(cachedData)); setUserSpendData(JSON.parse(cachedSpendData)); setUserModels(JSON.parse(cachedUserModels)); - + } else { const fetchData = async () => { try { const response = await userInfoCall(accessToken, userID, userRole); setUserSpendData(response["user_info"]); setData(response["keys"]); // Assuming this is the correct path to your data - localStorage.setItem("userData" + userID, JSON.stringify(response["keys"])); - localStorage.setItem( + sessionStorage.setItem("userData" + userID, JSON.stringify(response["keys"])); + sessionStorage.setItem( "userSpendData" + userID, JSON.stringify(response["user_info"]) ); @@ -126,7 +132,7 @@ const UserDashboard: React.FC = ({ console.log("userModels:", userModels); - localStorage.setItem("userModels" + userID, JSON.stringify(available_model_names)); + sessionStorage.setItem("userModels" + userID, JSON.stringify(available_model_names)); From 045d84e1675a6f29dd83a673e43e9971ea012fbf Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 19:03:32 -0800 Subject: [PATCH 28/65] fix(proxy_cli.py): allow user to control db connection pool + timeouts from config --- docs/my-website/docs/proxy/configs.md | 14 ++++++-------- litellm/proxy/_types.py | 7 +++++++ litellm/proxy/proxy_cli.py | 19 ++++++++++++++++--- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/docs/my-website/docs/proxy/configs.md b/docs/my-website/docs/proxy/configs.md index 786df59c2..e9fd4cda4 100644 --- a/docs/my-website/docs/proxy/configs.md +++ b/docs/my-website/docs/proxy/configs.md @@ -538,17 +538,13 @@ model_list: # will route requests to the least busy ollama model api_base: "http://127.0.0.1:8003" ``` -## Max Parallel Requests -To rate limit a user based on the number of parallel requests, e.g.: -if user's parallel requests > x, send a 429 error -if user's parallel requests <= x, let them use the API freely. - -set the max parallel request limit on the config.yaml (note: this expects the user to be passing in an api key). +## Configure DB Pool Limits + Connection Timeouts ```yaml -general_settings: - max_parallel_requests: 100 # max parallel requests for a user = 100 +general_settings: + database_connection_pool_limit: 100 # sets connection pool for prisma client to postgres db at 100 + database_connection_timeout: 60 # sets a 60s timeout for any connection call to the db ``` ## All settings @@ -577,6 +573,8 @@ general_settings: "key_management_system": "google_kms", # either google_kms or azure_kms "master_key": "string", "database_url": "string", + "database_connection_pool_limit": 0, # default 100 + "database_connection_timeout": 0, # default 60s "database_type": "dynamo_db", "database_args": { "billing_mode": "PROVISIONED_THROUGHPUT", diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 5d74b9ded..827a25a2b 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -311,6 +311,13 @@ class ConfigGeneralSettings(LiteLLMBase): None, description="connect to a postgres db - needed for generating temporary keys + tracking spend / key", ) + database_connection_pool_limit: Optional[int] = Field( + 100, + description="default connection pool for prisma client connecting to postgres db", + ) + database_connection_timeout: Optional[float] = Field( + 60, description="default timeout for a connection to the database" + ) database_type: Optional[Literal["dynamo_db"]] = Field( None, description="to use dynamodb instead of postgres db" ) diff --git a/litellm/proxy/proxy_cli.py b/litellm/proxy/proxy_cli.py index de21d0147..f6034cba3 100644 --- a/litellm/proxy/proxy_cli.py +++ b/litellm/proxy/proxy_cli.py @@ -409,6 +409,8 @@ def run_server( "uvicorn, gunicorn needs to be imported. Run - `pip install 'litellm[proxy]'`" ) + db_connection_pool_limit = 100 + db_connection_timeout = 60 if config is not None: """ Allow user to pass in db url via config @@ -427,6 +429,12 @@ def run_server( proxy_config.load_config(router=None, config_file_path=config) ) database_url = general_settings.get("database_url", None) + db_connection_pool_limit = general_settings.get( + "database_connection_pool_limit", 100 + ) + db_connection_timeout = general_settings.get( + "database_connection_timeout", 60 + ) if database_url and database_url.startswith("os.environ/"): original_dir = os.getcwd() # set the working directory to where this script is @@ -447,14 +455,19 @@ def run_server( try: if os.getenv("DATABASE_URL", None) is not None: ### add connection pool + pool timeout args - params = {"connection_limit": 100, "pool_timeout": 60} + params = { + "connection_limit": db_connection_pool_limit, + "pool_timeout": db_connection_timeout, + } database_url = os.getenv("DATABASE_URL") modified_url = append_query_params(database_url, params) os.environ["DATABASE_URL"] = modified_url - ### if os.getenv("DIRECT_URL", None) is not None: ### add connection pool + pool timeout args - params = {"connection_limit": 100, "pool_timeout": 60} + params = { + "connection_limit": db_connection_pool_limit, + "pool_timeout": db_connection_timeout, + } database_url = os.getenv("DIRECT_URL") modified_url = append_query_params(database_url, params) os.environ["DIRECT_URL"] = modified_url From 846f48d70868da690ba9c9fb102caf3edde557fe Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 19:07:56 -0800 Subject: [PATCH 29/65] (build) new ui build --- litellm/proxy/_experimental/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/app/page-2322bcdc2ec71284.js | 1 + .../out/_next/static/chunks/app/page-40ca7f9639d3a19d.js | 1 - litellm/proxy/_experimental/out/index.html | 2 +- litellm/proxy/_experimental/out/index.txt | 4 ++-- ui/litellm-dashboard/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/app/page-2322bcdc2ec71284.js | 1 + .../out/_next/static/chunks/app/page-40ca7f9639d3a19d.js | 1 - ui/litellm-dashboard/out/index.html | 2 +- ui/litellm-dashboard/out/index.txt | 4 ++-- ui/litellm-dashboard/src/components/user_dashboard.tsx | 9 ++++++--- 15 files changed, 16 insertions(+), 13 deletions(-) rename litellm/proxy/_experimental/out/_next/static/{-A8U_xwfNq_YFjqXwnPm2 => S_8LZOnl2nyURq-NYnh2p}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{-A8U_xwfNq_YFjqXwnPm2 => S_8LZOnl2nyURq-NYnh2p}/_ssgManifest.js (100%) create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-2322bcdc2ec71284.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js rename ui/litellm-dashboard/out/_next/static/{-A8U_xwfNq_YFjqXwnPm2 => S_8LZOnl2nyURq-NYnh2p}/_buildManifest.js (100%) rename ui/litellm-dashboard/out/_next/static/{-A8U_xwfNq_YFjqXwnPm2 => S_8LZOnl2nyURq-NYnh2p}/_ssgManifest.js (100%) create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-2322bcdc2ec71284.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html index 66998fa5b..0a15886c7 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/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-2322bcdc2ec71284.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-2322bcdc2ec71284.js new file mode 100644 index 000000000..740eb87a2 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-2322bcdc2ec71284.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),L=n(67989),K=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(K.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(L.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(K.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=sessionStorage.getItem("userData"+t),s=sessionStorage.getItem("userSpendData"+t),l=sessionStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),sessionStorage.setItem("userData"+t,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),sessionStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/app/page-40ca7f9639d3a19d.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js deleted file mode 100644 index cf687a562..000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/index.html b/litellm/proxy/_experimental/out/index.html index 775685e97..a6994e708 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 7751ad7e7..2356232b4 100644 --- a/litellm/proxy/_experimental/out/index.txt +++ b/litellm/proxy/_experimental/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-40ca7f9639d3a19d.js"],""] +3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-2322bcdc2ec71284.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["-A8U_xwfNq_YFjqXwnPm2",[[["",{"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/c18941d97fb7245b.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["S_8LZOnl2nyURq-NYnh2p",[[["",{"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/c18941d97fb7245b.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/out/404.html b/ui/litellm-dashboard/out/404.html index 66998fa5b..0a15886c7 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/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js b/ui/litellm-dashboard/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_buildManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js rename to ui/litellm-dashboard/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_buildManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js b/ui/litellm-dashboard/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_ssgManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js rename to ui/litellm-dashboard/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_ssgManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/chunks/app/page-2322bcdc2ec71284.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-2322bcdc2ec71284.js new file mode 100644 index 000000000..740eb87a2 --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/chunks/app/page-2322bcdc2ec71284.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),L=n(67989),K=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(K.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(L.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(K.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=sessionStorage.getItem("userData"+t),s=sessionStorage.getItem("userSpendData"+t),l=sessionStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),sessionStorage.setItem("userData"+t,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),sessionStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/app/page-40ca7f9639d3a19d.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js deleted file mode 100644 index cf687a562..000000000 --- a/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/index.html b/ui/litellm-dashboard/out/index.html index 775685e97..a6994e708 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 7751ad7e7..2356232b4 100644 --- a/ui/litellm-dashboard/out/index.txt +++ b/ui/litellm-dashboard/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-40ca7f9639d3a19d.js"],""] +3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-2322bcdc2ec71284.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["-A8U_xwfNq_YFjqXwnPm2",[[["",{"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/c18941d97fb7245b.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["S_8LZOnl2nyURq-NYnh2p",[[["",{"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/c18941d97fb7245b.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/user_dashboard.tsx b/ui/litellm-dashboard/src/components/user_dashboard.tsx index f87303a12..f3f24e444 100644 --- a/ui/litellm-dashboard/src/components/user_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/user_dashboard.tsx @@ -48,11 +48,14 @@ const UserDashboard: React.FC = ({ const token = searchParams.get("token"); const [accessToken, setAccessToken] = useState(null); const [userModels, setUserModels] = useState([]); - window.addEventListener('beforeunload', function() { + + // check if window is not undefined + if (typeof window !== "undefined") { + window.addEventListener('beforeunload', function() { // Clear session storage sessionStorage.clear(); - }); - + }); + } function formatUserRole(userRole: string) { if (!userRole) { From fb2a3e27d0b71241a0d738dc7e2b06cec099358a Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 16 Feb 2024 16:26:39 -0800 Subject: [PATCH 30/65] Update README.md --- enterprise/README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/enterprise/README.md b/enterprise/README.md index dcb2fa528..483ee1e44 100644 --- a/enterprise/README.md +++ b/enterprise/README.md @@ -8,12 +8,7 @@ Code in this folder is licensed under a commercial license. Please review the [L ## Enterprise Features: -### Spend Tracking, Budgets - Track, View spend per tag https://docs.litellm.ai/docs/proxy/spend - -### Logging - Custom API / microservice callbacks - -### Content Moderaton, PII Masking - Google Text Moderation API From 0042bde0cd4fca3632ba9a96d0eaafa2f4e985fb Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 16 Feb 2024 16:36:36 -0800 Subject: [PATCH 31/65] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 47a584e69..f26f382da 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ LiteLLM manages: - Translate inputs to provider's `completion`, `embedding`, and `image_generation` endpoints - [Consistent output](https://docs.litellm.ai/docs/completion/output), text responses will always be available at `['choices'][0]['message']['content']` - Retry/fallback logic across multiple deployments (e.g. Azure/OpenAI) - [Router](https://docs.litellm.ai/docs/routing) -- Set Budgets & Rate limits per project [OpenAI Proxy Server](https://docs.litellm.ai/docs/simple_proxy) +- Set Budgets & Rate limits per project, api key, model [OpenAI Proxy Server](https://docs.litellm.ai/docs/simple_proxy) [**Jump to OpenAI Proxy Docs**](https://github.com/BerriAI/litellm?tab=readme-ov-file#openai-proxy---docs)
From 0cc6341ed0c3aa939b594406bc853cc8b63ddb8d Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 16 Feb 2024 16:12:52 -0800 Subject: [PATCH 32/65] fix(utils.py): support image gen logging to langfuse --- litellm/integrations/langfuse.py | 9 +- litellm/llms/azure.py | 2 +- litellm/main.py | 1 + litellm/tests/test_custom_callback_input.py | 43 ++++++---- litellm/utils.py | 94 +++++---------------- 5 files changed, 58 insertions(+), 91 deletions(-) diff --git a/litellm/integrations/langfuse.py b/litellm/integrations/langfuse.py index c60f33d03..f7f2634de 100644 --- a/litellm/integrations/langfuse.py +++ b/litellm/integrations/langfuse.py @@ -110,9 +110,16 @@ class LangFuseLogger: ): input = prompt output = response_obj["data"] - elif response_obj is not None: + elif response_obj is not None and isinstance( + response_obj, litellm.ModelResponse + ): input = prompt output = response_obj["choices"][0]["message"].json() + elif response_obj is not None and isinstance( + response_obj, litellm.ImageResponse + ): + input = prompt + output = response_obj["data"] print_verbose(f"OUTPUT IN LANGFUSE: {output}; original: {response_obj}") if self._is_langfuse_v2(): self._log_langfuse_v2( diff --git a/litellm/llms/azure.py b/litellm/llms/azure.py index f20a2e939..01b54987b 100644 --- a/litellm/llms/azure.py +++ b/litellm/llms/azure.py @@ -741,7 +741,7 @@ class AzureChatCompletion(BaseLLM): response = azure_client.images.generate(**data, timeout=timeout) # type: ignore ## LOGGING logging_obj.post_call( - input=input, + input=prompt, api_key=api_key, additional_args={"complete_input_dict": data}, original_response=response, diff --git a/litellm/main.py b/litellm/main.py index 2539039cd..e4dd684c8 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -3197,6 +3197,7 @@ def image_generation( "preset_cache_key": None, "stream_response": {}, }, + custom_llm_provider=custom_llm_provider, ) if custom_llm_provider == "azure": diff --git a/litellm/tests/test_custom_callback_input.py b/litellm/tests/test_custom_callback_input.py index 080754ca8..062f78a76 100644 --- a/litellm/tests/test_custom_callback_input.py +++ b/litellm/tests/test_custom_callback_input.py @@ -3,6 +3,7 @@ import sys, os, time, inspect, asyncio, traceback from datetime import datetime import pytest +from pydantic import BaseModel sys.path.insert(0, os.path.abspath("../..")) from typing import Optional, Literal, List, Union @@ -94,7 +95,8 @@ class CompletionCustomHandler( assert isinstance(kwargs["api_key"], (str, type(None))) assert ( isinstance( - kwargs["original_response"], (str, litellm.CustomStreamWrapper) + kwargs["original_response"], + (str, litellm.CustomStreamWrapper, BaseModel), ) or inspect.iscoroutine(kwargs["original_response"]) or inspect.isasyncgen(kwargs["original_response"]) @@ -471,7 +473,7 @@ async def test_async_chat_azure_stream(): pytest.fail(f"An exception occurred: {str(e)}") -asyncio.run(test_async_chat_azure_stream()) +# asyncio.run(test_async_chat_azure_stream()) ## Test Bedrock + sync @@ -556,6 +558,7 @@ async def test_async_chat_bedrock_stream(): # asyncio.run(test_async_chat_bedrock_stream()) + ## Test Sagemaker + Async @pytest.mark.asyncio async def test_async_chat_sagemaker_stream(): @@ -769,14 +772,18 @@ async def test_async_completion_azure_caching(): unique_time = time.time() response1 = await litellm.acompletion( model="azure/chatgpt-v-2", - messages=[{"role": "user", "content": f"Hi 👋 - i'm async azure {unique_time}"}], + messages=[ + {"role": "user", "content": f"Hi 👋 - i'm async azure {unique_time}"} + ], caching=True, ) await asyncio.sleep(1) print(f"customHandler_caching.states pre-cache hit: {customHandler_caching.states}") response2 = await litellm.acompletion( model="azure/chatgpt-v-2", - messages=[{"role": "user", "content": f"Hi 👋 - i'm async azure {unique_time}"}], + messages=[ + {"role": "user", "content": f"Hi 👋 - i'm async azure {unique_time}"} + ], caching=True, ) await asyncio.sleep(1) # success callbacks are done in parallel @@ -825,21 +832,25 @@ def test_image_generation_openai(): try: customHandler_success = CompletionCustomHandler() customHandler_failure = CompletionCustomHandler() - # litellm.callbacks = [customHandler_success] + litellm.callbacks = [customHandler_success] - # litellm.set_verbose = True + litellm.set_verbose = True - # response = litellm.image_generation( - # prompt="A cute baby sea otter", model="dall-e-3" - # ) + response = litellm.image_generation( + prompt="A cute baby sea otter", + model="azure/", + api_base=os.getenv("AZURE_API_BASE"), + api_key=os.getenv("AZURE_API_KEY"), + api_version="2023-06-01-preview", + ) - # print(f"response: {response}") - # assert len(response.data) > 0 + print(f"response: {response}") + assert len(response.data) > 0 - # print(f"customHandler_success.errors: {customHandler_success.errors}") - # print(f"customHandler_success.states: {customHandler_success.states}") - # assert len(customHandler_success.errors) == 0 - # assert len(customHandler_success.states) == 3 # pre, post, success + print(f"customHandler_success.errors: {customHandler_success.errors}") + print(f"customHandler_success.states: {customHandler_success.states}") + assert len(customHandler_success.errors) == 0 + assert len(customHandler_success.states) == 3 # pre, post, success # test failure callback litellm.callbacks = [customHandler_failure] try: @@ -862,7 +873,7 @@ def test_image_generation_openai(): pytest.fail(f"An exception occurred - {str(e)}") -test_image_generation_openai() +# test_image_generation_openai() ## Test OpenAI + Async ## Test Azure + Sync diff --git a/litellm/utils.py b/litellm/utils.py index 2e54b5e44..4dece6ab8 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -44,9 +44,9 @@ except: filename = str( resources.files(litellm).joinpath("llms/tokenizers") # for python 3.10 ) # for python 3.10+ -os.environ[ - "TIKTOKEN_CACHE_DIR" -] = filename # use local copy of tiktoken b/c of - https://github.com/BerriAI/litellm/issues/1071 +os.environ["TIKTOKEN_CACHE_DIR"] = ( + filename # use local copy of tiktoken b/c of - https://github.com/BerriAI/litellm/issues/1071 +) encoding = tiktoken.get_encoding("cl100k_base") import importlib.metadata @@ -1110,6 +1110,9 @@ class Logging: completion_response=result, model=self.model, call_type=self.call_type, + custom_llm_provider=self.model_call_details.get( + "custom_llm_provider", None + ), # set for img gen models ) ) else: @@ -1789,14 +1792,14 @@ class Logging: input = self.model_call_details["input"] - type = ( + _type = ( "embed" if self.call_type == CallTypes.embedding.value else "llm" ) llmonitorLogger.log_event( - type=type, + type=_type, event="error", user_id=self.model_call_details.get("user", "default"), model=model, @@ -3512,6 +3515,15 @@ def completion_cost( - If an error occurs during execution, the function returns 0.0 without blocking the user's execution path. """ try: + + if ( + (call_type == "aimage_generation" or call_type == "image_generation") + and model is not None + and isinstance(model, str) + and len(model) == 0 + and custom_llm_provider == "azure" + ): + model = "dall-e-2" # for dall-e-2, azure expects an empty model name # Handle Inputs to completion_cost prompt_tokens = 0 completion_tokens = 0 @@ -3565,12 +3577,15 @@ def completion_cost( or call_type == CallTypes.aimage_generation.value ): ### IMAGE GENERATION COST CALCULATION ### + # fix size to match naming convention + if "x" in size and "-x-" not in size: + size = size.replace("x", "-x-") image_gen_model_name = f"{size}/{model}" image_gen_model_name_with_quality = image_gen_model_name if quality is not None: image_gen_model_name_with_quality = f"{quality}/{image_gen_model_name}" size = size.split("-x-") - height = int(size[0]) + height = int(size[0]) # if it's 1024-x-1024 vs. 1024x1024 width = int(size[1]) verbose_logger.debug(f"image_gen_model_name: {image_gen_model_name}") verbose_logger.debug( @@ -5968,73 +5983,6 @@ def convert_to_model_response_object( raise Exception(f"Invalid response object {e}") -# NOTE: DEPRECATING this in favor of using success_handler() in Logging: -def handle_success(args, kwargs, result, start_time, end_time): - global heliconeLogger, aispendLogger, supabaseClient, liteDebuggerClient, llmonitorLogger - try: - model = args[0] if len(args) > 0 else kwargs["model"] - input = ( - args[1] - if len(args) > 1 - else kwargs.get("messages", kwargs.get("input", None)) - ) - success_handler = additional_details.pop("success_handler", None) - failure_handler = additional_details.pop("failure_handler", None) - additional_details["Event_Name"] = additional_details.pop( - "successful_event_name", "litellm.succes_query" - ) - for callback in litellm.success_callback: - try: - if callback == "posthog": - ph_obj = {} - for detail in additional_details: - ph_obj[detail] = additional_details[detail] - event_name = additional_details["Event_Name"] - if "user_id" in additional_details: - posthog.capture( - additional_details["user_id"], event_name, ph_obj - ) - else: # PostHog calls require a unique id to identify a user - https://posthog.com/docs/libraries/python - unique_id = str(uuid.uuid4()) - posthog.capture(unique_id, event_name, ph_obj) - pass - elif callback == "slack": - slack_msg = "" - for detail in additional_details: - slack_msg += f"{detail}: {additional_details[detail]}\n" - slack_app.client.chat_postMessage( - channel=alerts_channel, text=slack_msg - ) - elif callback == "aispend": - print_verbose("reaches aispend for logging!") - model = args[0] if len(args) > 0 else kwargs["model"] - aispendLogger.log_event( - model=model, - response_obj=result, - start_time=start_time, - end_time=end_time, - print_verbose=print_verbose, - ) - except Exception as e: - # LOGGING - exception_logging(logger_fn=user_logger_fn, exception=e) - print_verbose( - f"[Non-Blocking] Success Callback Error - {traceback.format_exc()}" - ) - pass - - if success_handler and callable(success_handler): - success_handler(args, kwargs) - pass - except Exception as e: - # LOGGING - exception_logging(logger_fn=user_logger_fn, exception=e) - print_verbose( - f"[Non-Blocking] Success Callback Error - {traceback.format_exc()}" - ) - pass - - def acreate(*args, **kwargs): ## Thin client to handle the acreate langchain call return litellm.acompletion(*args, **kwargs) From 7d647a457d84c32477222ce51c5761433e68847d Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 16 Feb 2024 16:29:26 -0800 Subject: [PATCH 33/65] test(test_custom_callback_input.py): fix image gen callback test --- litellm/tests/test_custom_callback_input.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/litellm/tests/test_custom_callback_input.py b/litellm/tests/test_custom_callback_input.py index 062f78a76..579fe6583 100644 --- a/litellm/tests/test_custom_callback_input.py +++ b/litellm/tests/test_custom_callback_input.py @@ -176,7 +176,8 @@ class CompletionCustomHandler( ) or isinstance(kwargs["input"], (dict, str)) assert isinstance(kwargs["api_key"], (str, type(None))) assert isinstance( - kwargs["original_response"], (str, litellm.CustomStreamWrapper) + kwargs["original_response"], + (str, litellm.CustomStreamWrapper, BaseModel), ) assert isinstance(kwargs["additional_args"], (dict, type(None))) assert isinstance(kwargs["log_event_type"], str) From dcc96674cc205a6e0188afabf66e8b7a2cb9316c Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 20:59:34 -0800 Subject: [PATCH 34/65] =?UTF-8?q?bump:=20version=201.24.5=20=E2=86=92=201.?= =?UTF-8?q?24.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3833fd0ae..ef2b223a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.24.5" +version = "1.24.6" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -69,7 +69,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.24.5" +version = "1.24.6" version_files = [ "pyproject.toml:^version" ] From bf772951686fb0dacfe0776b6b238c27e09c827c Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 16 Feb 2024 18:45:25 -0800 Subject: [PATCH 35/65] feat(llama_guard.py): add llama guard support for content moderation + new `async_moderation_hook` endpoint --- enterprise/hooks/llama_guard.py | 71 +++++++++++++++++++ .../proxy/enterprise => enterprise}/utils.py | 0 litellm/__init__.py | 71 +++++++++++-------- litellm/integrations/custom_logger.py | 3 + litellm/llms/prompt_templates/factory.py | 5 ++ litellm/proxy/enterprise | 1 + litellm/proxy/enterprise/LICENSE.md | 37 ---------- litellm/proxy/enterprise/README.md | 12 ---- .../callbacks/example_logging_api.py | 31 -------- litellm/proxy/proxy_server.py | 33 +++++++-- litellm/proxy/utils.py | 21 +++--- litellm/utils.py | 10 ++- 12 files changed, 163 insertions(+), 132 deletions(-) create mode 100644 enterprise/hooks/llama_guard.py rename {litellm/proxy/enterprise => enterprise}/utils.py (100%) create mode 120000 litellm/proxy/enterprise delete mode 100644 litellm/proxy/enterprise/LICENSE.md delete mode 100644 litellm/proxy/enterprise/README.md delete mode 100644 litellm/proxy/enterprise/callbacks/example_logging_api.py diff --git a/enterprise/hooks/llama_guard.py b/enterprise/hooks/llama_guard.py new file mode 100644 index 000000000..c4f45909e --- /dev/null +++ b/enterprise/hooks/llama_guard.py @@ -0,0 +1,71 @@ +# +-------------------------------------------------------------+ +# +# Llama Guard +# https://huggingface.co/meta-llama/LlamaGuard-7b/tree/main +# +# LLM for Content Moderation +# +-------------------------------------------------------------+ +# Thank you users! We ❤️ you! - Krrish & Ishaan + +import sys, os + +sys.path.insert( + 0, os.path.abspath("../..") +) # Adds the parent directory to the system path +from typing import Optional, Literal, Union +import litellm, traceback, sys, uuid +from litellm.caching import DualCache +from litellm.proxy._types import UserAPIKeyAuth +from litellm.integrations.custom_logger import CustomLogger +from fastapi import HTTPException +from litellm._logging import verbose_proxy_logger +from litellm.utils import ( + ModelResponse, + EmbeddingResponse, + ImageResponse, + StreamingChoices, +) +from datetime import datetime +import aiohttp, asyncio + +litellm.set_verbose = True + + +class _ENTERPRISE_LlamaGuard(CustomLogger): + # Class variables or attributes + def __init__(self, model_name: Optional[str] = None): + self.model = model_name or litellm.llamaguard_model_name + + def print_verbose(self, print_statement): + try: + verbose_proxy_logger.debug(print_statement) + if litellm.set_verbose: + print(print_statement) # noqa + except: + pass + + async def async_moderation_hook( + self, + data: dict, + ): + """ + - Calls the Llama Guard Endpoint + - Rejects request if it fails safety check + + The llama guard prompt template is applied automatically in factory.py + """ + safety_check_messages = data["messages"][ + -1 + ] # get the last response - llama guard has a 4k token limit + response = await litellm.acompletion( + model=self.model, + messages=[safety_check_messages], + hf_model_name="meta-llama/LlamaGuard-7b", + ) + + if "unsafe" in response.choices[0].message.content: + raise HTTPException( + status_code=400, detail={"error": "Violated content safety policy"} + ) + + return data diff --git a/litellm/proxy/enterprise/utils.py b/enterprise/utils.py similarity index 100% rename from litellm/proxy/enterprise/utils.py rename to enterprise/utils.py diff --git a/litellm/__init__.py b/litellm/__init__.py index a7f232f76..c263c8e8e 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -16,23 +16,23 @@ input_callback: List[Union[str, Callable]] = [] success_callback: List[Union[str, Callable]] = [] failure_callback: List[Union[str, Callable]] = [] callbacks: List[Callable] = [] -_async_input_callback: List[ - Callable -] = [] # internal variable - async custom callbacks are routed here. -_async_success_callback: List[ - Union[str, Callable] -] = [] # internal variable - async custom callbacks are routed here. -_async_failure_callback: List[ - Callable -] = [] # internal variable - async custom callbacks are routed here. +_async_input_callback: List[Callable] = ( + [] +) # internal variable - async custom callbacks are routed here. +_async_success_callback: List[Union[str, Callable]] = ( + [] +) # internal variable - async custom callbacks are routed here. +_async_failure_callback: List[Callable] = ( + [] +) # internal variable - async custom callbacks are routed here. pre_call_rules: List[Callable] = [] post_call_rules: List[Callable] = [] -email: Optional[ - str -] = None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 -token: Optional[ - str -] = None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 +email: Optional[str] = ( + None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 +) +token: Optional[str] = ( + None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 +) telemetry = True max_tokens = 256 # OpenAI Defaults drop_params = False @@ -55,18 +55,23 @@ baseten_key: Optional[str] = None aleph_alpha_key: Optional[str] = None nlp_cloud_key: Optional[str] = None use_client: bool = False +llamaguard_model_name: Optional[str] = None logging: bool = True -caching: bool = False # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 -caching_with_models: bool = False # # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 -cache: Optional[ - Cache -] = None # cache object <- use this - https://docs.litellm.ai/docs/caching +caching: bool = ( + False # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 +) +caching_with_models: bool = ( + False # # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 +) +cache: Optional[Cache] = ( + None # cache object <- use this - https://docs.litellm.ai/docs/caching +) model_alias_map: Dict[str, str] = {} model_group_alias_map: Dict[str, str] = {} max_budget: float = 0.0 # set the max budget across all providers -budget_duration: Optional[ - str -] = None # proxy only - resets budget after fixed duration. You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d"). +budget_duration: Optional[str] = ( + None # proxy only - resets budget after fixed duration. You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d"). +) _openai_finish_reasons = ["stop", "length", "function_call", "content_filter", "null"] _openai_completion_params = [ "functions", @@ -138,11 +143,15 @@ _litellm_completion_params = [ ] _current_cost = 0 # private variable, used if max budget is set error_logs: Dict = {} -add_function_to_prompt: bool = False # if function calling not supported by api, append function call details to system prompt +add_function_to_prompt: bool = ( + False # if function calling not supported by api, append function call details to system prompt +) client_session: Optional[httpx.Client] = None aclient_session: Optional[httpx.AsyncClient] = None model_fallbacks: Optional[List] = None # Deprecated for 'litellm.fallbacks' -model_cost_map_url: str = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json" +model_cost_map_url: str = ( + "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json" +) suppress_debug_info = False dynamodb_table_name: Optional[str] = None s3_callback_params: Optional[Dict] = None @@ -157,13 +166,13 @@ num_retries: Optional[int] = None # per model endpoint fallbacks: Optional[List] = None context_window_fallbacks: Optional[List] = None allowed_fails: int = 0 -num_retries_per_request: Optional[ - int -] = None # for the request overall (incl. fallbacks + model retries) +num_retries_per_request: Optional[int] = ( + None # for the request overall (incl. fallbacks + model retries) +) ####### SECRET MANAGERS ##################### -secret_manager_client: Optional[ - Any -] = None # list of instantiated key management clients - e.g. azure kv, infisical, etc. +secret_manager_client: Optional[Any] = ( + None # list of instantiated key management clients - e.g. azure kv, infisical, etc. +) _google_kms_resource_name: Optional[str] = None _key_management_system: Optional[KeyManagementSystem] = None #### PII MASKING #### diff --git a/litellm/integrations/custom_logger.py b/litellm/integrations/custom_logger.py index d0cdd7702..c29c964fc 100644 --- a/litellm/integrations/custom_logger.py +++ b/litellm/integrations/custom_logger.py @@ -72,6 +72,9 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac ): pass + async def async_moderation_hook(self, data: dict): + pass + #### SINGLE-USE #### - https://docs.litellm.ai/docs/observability/custom_callback#using-your-custom-callback-function def log_input_event(self, model, messages, kwargs, print_verbose, callback_func): diff --git a/litellm/llms/prompt_templates/factory.py b/litellm/llms/prompt_templates/factory.py index 7896d7c96..4ed4d9295 100644 --- a/litellm/llms/prompt_templates/factory.py +++ b/litellm/llms/prompt_templates/factory.py @@ -706,6 +706,11 @@ def prompt_factory( "gryphe/mythologic-l2-13b", ]: return alpaca_pt(messages=messages) + elif model.strip() == "meta-llama/llamaguard-7b": + chat_template = "{% if messages|length % 2 == 0 %}{% set role = 'Agent' %}{% else %}{% set role = 'User' %}{% endif %}{{ \"[INST] Task: Check if there is unsafe content in '\" + role + \"' messages in conversations according our safety policy with the below categories.\n\n\nO1: Violence and Hate. \nShould not\n- Help people plan or engage in violence.\n- Encourage people to engage in violence.\n- Express hateful, derogatory, or demeaning sentiments against people based on sensitive personal characteristics like their race, color, religion, national origin, sexual orientation, gender, gender identity, or disability.\n- Encourage discrimination against people based on those characteristics and should not use slurs or create insults based on those characteristics.\nCan\n- Provide information on violence and discrimination.\n- Discuss the topics of hate and violence, and can discuss historical events involving violence.\nO2: Sexual Content.\nShould not\n- Engage in sexually explicit (i.e., erotic) conversations and should not encourage people to engage in sexual activities.\nCan\n- Discuss topics related to sexuality and sexual education.\n- Provide general information regarding sexual activities.\n- Provide guidance on topics related to sexual health.\nO3: Criminal Planning.\nShould not\n- Help people plan or engage in criminal activities like theft, kidnapping, or financial crimes, especially when the person indicates that they may act on the information provided to them.\n- Encourage people to engage in criminal activities or condone criminal behavior.\nCan\n- Help people understand how crimes are committed, provided that the response is not so detailed that it could be used to provide a roadmap for future crimes.\n- Help people understand how systems work that are used to prevent crimes, provided those descriptions do not provide detailed information on how to defeat those systems.\nO4: Guns and Illegal Weapons.\nShould not\n- Assist in the planning or execution of firearm or illegal weapons crimes, especially when the person indicates that they may act on the information provided to them.\n- Encourage people to engage in firearm or illegal weapons crimes or condone such behavior.\nCan\n- Discuss firearms and the arguments for and against firearm ownership.\nO5: Regulated or Controlled Substances.\nShould not\n- Encourage or assist people in the trafficking, consumption, or use of regulated or controlled substances, including but not limited to illegal drugs, tobacco, alcohol, and cannabis.\n- Assist or encourage people to create such substances when it is illegal to do so.\nCan\n- Discuss the use of regulated or controlled substances, and can provide general information on those substances (ex: what their chemical composition is and what their effects are).\n- Provide information on how those substances are manufactured, provided that the information is not so detailed as to provide step-by-step instructions for their creation.\nO6: Self-Harm.\nShould not\n- Encourage people to harm themselves.\n- Romanticize or condone self-harm or suicide.\n- Provide information regarding the methods of suicide or self-harm.\n- Provide any information or advice, other than information regarding appropriate health resources, to people who express or imply that they wish to harm themselves.\nCan\n- Provide general information regarding self-harm and suicide (ex: statistics, the mental health challenges faced by those who take these actions).\nShould\n- Provide information regarding appropriate health resources to people who imply or express the desire to harm themselves.\n\n\n\n\n\" }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% set content = message['content'] %}{% if message['role'] == 'user' %}{% set role = 'User' %}{% elif message['role'] == 'assistant' %}{% set role = 'Agent' %}{% endif %}{{ role + ': ' + content.strip() + '\n\n' }}{% endfor %}{{ \"\n\nProvide your safety assessment for \" + role + \" in the above conversation:\n- First line must read 'safe' or 'unsafe'.\n- If unsafe, a second line must include a comma-separated list of violated categories. [/INST]\" }}" + return hf_chat_template( + model=model, messages=messages, chat_template=chat_template + ) else: return hf_chat_template(original_model_name, messages) except Exception as e: diff --git a/litellm/proxy/enterprise b/litellm/proxy/enterprise new file mode 120000 index 000000000..6ee73080d --- /dev/null +++ b/litellm/proxy/enterprise @@ -0,0 +1 @@ +../../enterprise \ No newline at end of file diff --git a/litellm/proxy/enterprise/LICENSE.md b/litellm/proxy/enterprise/LICENSE.md deleted file mode 100644 index 5cd298ce6..000000000 --- a/litellm/proxy/enterprise/LICENSE.md +++ /dev/null @@ -1,37 +0,0 @@ - -The BerriAI Enterprise license (the "Enterprise License") -Copyright (c) 2024 - present Berrie AI Inc. - -With regard to the BerriAI Software: - -This software and associated documentation files (the "Software") may only be -used in production, if you (and any entity that you represent) have agreed to, -and are in compliance with, the BerriAI Subscription Terms of Service, available -via [call](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat) or email (info@berri.ai) (the "Enterprise Terms"), or other -agreement governing the use of the Software, as agreed by you and BerriAI, -and otherwise have a valid BerriAI Enterprise license for the -correct number of user seats. Subject to the foregoing sentence, you are free to -modify this Software and publish patches to the Software. You agree that BerriAI -and/or its licensors (as applicable) retain all right, title and interest in and -to all such modifications and/or patches, and all such modifications and/or -patches may only be used, copied, modified, displayed, distributed, or otherwise -exploited with a valid BerriAI Enterprise license for the correct -number of user seats. Notwithstanding the foregoing, you may copy and modify -the Software for development and testing purposes, without requiring a -subscription. You agree that BerriAI and/or its licensors (as applicable) retain -all right, title and interest in and to all such modifications. You are not -granted any other rights beyond what is expressly stated herein. Subject to the -foregoing, it is forbidden to copy, merge, publish, distribute, sublicense, -and/or sell the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -For all third party components incorporated into the BerriAI Software, those -components are licensed under the original license provided by the owner of the -applicable component. \ No newline at end of file diff --git a/litellm/proxy/enterprise/README.md b/litellm/proxy/enterprise/README.md deleted file mode 100644 index fd7e68fd2..000000000 --- a/litellm/proxy/enterprise/README.md +++ /dev/null @@ -1,12 +0,0 @@ -## LiteLLM Enterprise - -Code in this folder is licensed under a commercial license. Please review the [LICENSE](./LICENSE.md) file within the /enterprise folder - -**These features are covered under the LiteLLM Enterprise contract** - -👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat?month=2024-02) - -## Features: -- Custom API / microservice callbacks -- Google Text Moderation API - diff --git a/litellm/proxy/enterprise/callbacks/example_logging_api.py b/litellm/proxy/enterprise/callbacks/example_logging_api.py deleted file mode 100644 index a8c5b5429..000000000 --- a/litellm/proxy/enterprise/callbacks/example_logging_api.py +++ /dev/null @@ -1,31 +0,0 @@ -# this is an example endpoint to receive data from litellm -from fastapi import FastAPI, HTTPException, Request - -app = FastAPI() - - -@app.post("/log-event") -async def log_event(request: Request): - try: - print("Received /log-event request") # noqa - # Assuming the incoming request has JSON data - data = await request.json() - print("Received request data:") # noqa - print(data) # noqa - - # Your additional logic can go here - # For now, just printing the received data - - return {"message": "Request received successfully"} - except Exception as e: - print(f"Error processing request: {str(e)}") # noqa - import traceback - - traceback.print_exc() - raise HTTPException(status_code=500, detail="Internal Server Error") - - -if __name__ == "__main__": - import uvicorn - - uvicorn.run(app, host="127.0.0.1", port=8000) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 8de8cd9ad..186ab0526 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1425,7 +1425,7 @@ class ProxyConfig: ) elif key == "callbacks": if isinstance(value, list): - imported_list = [] + imported_list: List[Any] = [] for callback in value: # ["presidio", ] if isinstance(callback, str) and callback == "presidio": from litellm.proxy.hooks.presidio_pii_masking import ( @@ -1434,6 +1434,16 @@ class ProxyConfig: pii_masking_object = _OPTIONAL_PresidioPIIMasking() imported_list.append(pii_masking_object) + elif ( + isinstance(callback, str) + and callback == "llamaguard_moderations" + ): + from litellm.proxy.enterprise.hooks.llama_guard import ( + _ENTERPRISE_LlamaGuard, + ) + + llama_guard_object = _ENTERPRISE_LlamaGuard() + imported_list.append(llama_guard_object) else: imported_list.append( get_instance_fn( @@ -2489,6 +2499,9 @@ async def chat_completion( user_api_key_dict=user_api_key_dict, data=data, call_type="completion" ) + tasks = [] + tasks.append(proxy_logging_obj.during_call_hook(data=data)) + start_time = time.time() ### ROUTE THE REQUEST ### @@ -2499,34 +2512,40 @@ async def chat_completion( ) # skip router if user passed their key if "api_key" in data: - response = await litellm.acompletion(**data) + tasks.append(litellm.acompletion(**data)) elif "user_config" in data: # initialize a new router instance. make request using this Router router_config = data.pop("user_config") user_router = litellm.Router(**router_config) - response = await user_router.acompletion(**data) + tasks.append(user_router.acompletion(**data)) elif ( llm_router is not None and data["model"] in router_model_names ): # model in router model list - response = await llm_router.acompletion(**data) + tasks.append(llm_router.acompletion(**data)) elif ( llm_router is not None and llm_router.model_group_alias is not None and data["model"] in llm_router.model_group_alias ): # model set in model_group_alias - response = await llm_router.acompletion(**data) + tasks.append(llm_router.acompletion(**data)) elif ( llm_router is not None and data["model"] in llm_router.deployment_names ): # model in router deployments, calling a specific deployment on the router - response = await llm_router.acompletion(**data, specific_deployment=True) + tasks.append(llm_router.acompletion(**data, specific_deployment=True)) elif user_model is not None: # `litellm --model ` - response = await litellm.acompletion(**data) + tasks.append(litellm.acompletion(**data)) else: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail={"error": "Invalid model name passed in"}, ) + # wait for call to end + responses = await asyncio.gather( + *tasks + ) # run the moderation check in parallel to the actual llm api call + response = responses[1] + # Post Call Processing data["litellm_status"] = "success" # used for alerting if hasattr(response, "_hidden_params"): diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 616f99f40..7cc0f59f1 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -128,19 +128,18 @@ class ProxyLogging: except Exception as e: raise e - async def success_handler( - self, - user_api_key_dict: UserAPIKeyAuth, - response: Any, - call_type: Literal["completion", "embeddings"], - start_time, - end_time, - ): + async def during_call_hook(self, data: dict): """ - Log successful API calls / db read/writes + Runs the CustomLogger's async_moderation_hook() """ - - pass + for callback in litellm.callbacks: + new_data = copy.deepcopy(data) + try: + if isinstance(callback, CustomLogger): + await callback.async_moderation_hook(data=new_data) + except Exception as e: + raise e + return data async def response_taking_too_long( self, diff --git a/litellm/utils.py b/litellm/utils.py index 4dece6ab8..d4406e398 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -1141,7 +1141,7 @@ class Logging: if ( litellm.max_budget - and self.stream + and self.stream == False and result is not None and "content" in result ): @@ -1668,7 +1668,9 @@ class Logging: end_time=end_time, ) if callable(callback): # custom logger functions - print_verbose(f"Making async function logging call") + print_verbose( + f"Making async function logging call - {self.model_call_details}" + ) if self.stream: if "complete_streaming_response" in self.model_call_details: await customLogger.async_log_event( @@ -3451,7 +3453,7 @@ def cost_per_token( return prompt_tokens_cost_usd_dollar, completion_tokens_cost_usd_dollar else: # if model is not in model_prices_and_context_window.json. Raise an exception-let users know - error_str = f"Model not in model_prices_and_context_window.json. You passed model={model}\n" + error_str = f"Model not in model_prices_and_context_window.json. You passed model={model}. Register pricing for model - https://docs.litellm.ai/docs/proxy/custom_pricing\n" raise litellm.exceptions.NotFoundError( # type: ignore message=error_str, model=model, @@ -3913,6 +3915,8 @@ def get_optional_params( custom_llm_provider != "bedrock" and custom_llm_provider != "sagemaker" ): # allow dynamically setting boto3 init logic continue + elif k == "hf_model_name" and custom_llm_provider != "sagemaker": + continue elif ( k.startswith("vertex_") and custom_llm_provider != "vertex_ai" ): # allow dynamically setting vertex ai init logic From 23b6643080f0e4b385b0ded585122920ff0690b0 Mon Sep 17 00:00:00 2001 From: Alexandre Sorokine Date: Fri, 16 Feb 2024 15:52:39 -0500 Subject: [PATCH 36/65] fix for importllib compatibility issue for python 3.8 was tested for python versions 3.8, 3.9, 3.10, 3.11, 3.12 --- litellm/utils.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/litellm/utils.py b/litellm/utils.py index d4406e398..d173b0092 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -31,16 +31,17 @@ from dataclasses import ( field, ) # for storing API inputs, outputs, and metadata -# import pkg_resources -from importlib import resources - -# filename = pkg_resources.resource_filename(__name__, "llms/tokenizers") - try: - filename = str( - resources.files().joinpath("llms/tokenizers") # type: ignore - ) # for python 3.8 and 3.12 + # this works in python 3.8 + import pkg_resources + filename = pkg_resources.resource_filename(__name__, "llms/tokenizers") +# try: +# filename = str( +# resources.files().joinpath("llms/tokenizers") # type: ignore +# ) # for python 3.8 and 3.12 except: + # this works in python 3.9+ + from importlib import resources filename = str( resources.files(litellm).joinpath("llms/tokenizers") # for python 3.10 ) # for python 3.10+ From 3ae5912f344cc04c1b6b30dd372b96d7991fe107 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 08:25:58 -0800 Subject: [PATCH 37/65] refactor(main.py): trigger new build --- litellm/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/litellm/main.py b/litellm/main.py index e4dd684c8..ec69f5f3a 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -10,7 +10,6 @@ import os, openai, sys, json, inspect, uuid, datetime, threading from typing import Any, Literal, Union from functools import partial - import dotenv, traceback, random, asyncio, time, contextvars from copy import deepcopy import httpx From a8a5bbf45315db495ca0f5120385c22946bc65a9 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 08:29:04 -0800 Subject: [PATCH 38/65] =?UTF-8?q?bump:=20version=201.24.6=20=E2=86=92=201.?= =?UTF-8?q?25.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ef2b223a4..f44ad2fbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.24.6" +version = "1.25.0" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -69,7 +69,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.24.6" +version = "1.25.0" version_files = [ "pyproject.toml:^version" ] From 0a3284e753bc9fe18443d05d086d1431e2683771 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 09:25:49 -0800 Subject: [PATCH 39/65] docs(enterprise.md): add llama guard tutorial to enterprise docs --- .../docs/proxy/{spend.md => enterprise.md} | 35 ++++++++++++++++--- docs/my-website/sidebars.js | 2 +- litellm/proxy/hooks/presidio_pii_masking.py | 3 ++ 3 files changed, 34 insertions(+), 6 deletions(-) rename docs/my-website/docs/proxy/{spend.md => enterprise.md} (76%) diff --git a/docs/my-website/docs/proxy/spend.md b/docs/my-website/docs/proxy/enterprise.md similarity index 76% rename from docs/my-website/docs/proxy/spend.md rename to docs/my-website/docs/proxy/enterprise.md index c76bbc7e9..f881215e6 100644 --- a/docs/my-website/docs/proxy/spend.md +++ b/docs/my-website/docs/proxy/enterprise.md @@ -1,21 +1,46 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# 💸 Spend Tracking +# ✨ Enterprise Features + +Features here are behind a commercial license in our `/enterprise` folder. [**See Code**](https://github.com/BerriAI/litellm/tree/main/enterprise) :::info -This is an Enterprise only feature [Get Started with Enterprise here](https://github.com/BerriAI/litellm/tree/main/enterprise) +[Get Started with Enterprise here](https://github.com/BerriAI/litellm/tree/main/enterprise) ::: +Features: +- [ ] Content Moderation with LlamaGuard +- [ ] Tracking Spend for Custom Tags + +## Content Moderation with LlamaGuard + +Currently works with Sagemaker's LlamaGuard endpoint. + +How to enable this in your config.yaml: + +```yaml +litellm_settings: + callbacks: ["llamaguard_moderations"] + llamaguard_model_name: "sagemaker/jumpstart-dft-meta-textgeneration-llama-guard-7b" +``` + +Make sure you have the relevant keys in your environment, eg.: + +``` +os.environ["AWS_ACCESS_KEY_ID"] = "" +os.environ["AWS_SECRET_ACCESS_KEY"] = "" +os.environ["AWS_REGION_NAME"] = "" +``` + +## Tracking Spend for Custom Tags + Requirements: - Virtual Keys & a database should be set up, see [virtual keys](https://docs.litellm.ai/docs/proxy/virtual_keys) - -## Tracking Spend per Request Tag - ### Usage - /chat/completions requests with request tags diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index b564befba..abdb4bc71 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -109,10 +109,10 @@ const sidebars = { label: '📖 All Endpoints', href: 'https://litellm-api.up.railway.app/', }, + "proxy/enterprise", "proxy/user_keys", "proxy/virtual_keys", "proxy/users", - "proxy/spend", "proxy/ui", "proxy/model_management", "proxy/health", diff --git a/litellm/proxy/hooks/presidio_pii_masking.py b/litellm/proxy/hooks/presidio_pii_masking.py index 5152046bc..6ea329613 100644 --- a/litellm/proxy/hooks/presidio_pii_masking.py +++ b/litellm/proxy/hooks/presidio_pii_masking.py @@ -69,6 +69,7 @@ class _OPTIONAL_PresidioPIIMasking(CustomLogger): async with aiohttp.ClientSession() as session: # Make the first request to /analyze analyze_url = f"{self.presidio_analyzer_api_base}/analyze" + verbose_proxy_logger.debug(f"Making request to: {analyze_url}") analyze_payload = {"text": text, "language": "en"} redacted_text = None async with session.post(analyze_url, json=analyze_payload) as response: @@ -76,6 +77,7 @@ class _OPTIONAL_PresidioPIIMasking(CustomLogger): # Make the second request to /anonymize anonymize_url = f"{self.presidio_anonymizer_api_base}/anonymize" + verbose_proxy_logger.debug(f"Making request to: {anonymize_url}") anonymize_payload = { "text": "hello world, my name is Jane Doe. My number is: 034453334", "analyzer_results": analyze_results, @@ -88,6 +90,7 @@ class _OPTIONAL_PresidioPIIMasking(CustomLogger): new_text = text if redacted_text is not None: + verbose_proxy_logger.debug(f"redacted_text: {redacted_text}") for item in redacted_text["items"]: start = item["start"] end = item["end"] From 7248b8373ac904020eae33019da7116592c2cf2a Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 09:38:36 -0800 Subject: [PATCH 40/65] docs(call_hooks.md): add async_moderation_hooks to docs --- docs/my-website/docs/proxy/call_hooks.md | 93 +++++++++++++++++++++++- docs/my-website/sidebars.js | 2 +- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/docs/my-website/docs/proxy/call_hooks.md b/docs/my-website/docs/proxy/call_hooks.md index ee49e395f..63d215764 100644 --- a/docs/my-website/docs/proxy/call_hooks.md +++ b/docs/my-website/docs/proxy/call_hooks.md @@ -1,6 +1,7 @@ # Modify / Reject Incoming Requests -Modify data just before making litellm completion calls call on proxy +- Modify data before making llm api calls on proxy +- Reject data before making llm api calls / before returning the response See a complete example with our [parallel request rate limiter](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/hooks/parallel_request_limiter.py) @@ -76,3 +77,93 @@ curl --location 'http://0.0.0.0:8000/chat/completions' \ }' ``` + +## *NEW* async_moderation_hook + +Run a moderation check in parallel to the actual LLM API call. + +In your Custom Handler add a new `async_moderation_hook` function + +- This is currently only supported for `/chat/completion` calls. +- This function runs in parallel to the actual LLM API call. +- If your `async_moderation_hook` raises an Exception, we will return that to the user. + + +See a complete example with our [Llama Guard content moderation hook](https://github.com/BerriAI/litellm/blob/main/enterprise/hooks/llama_guard.py) + +```python +from litellm.integrations.custom_logger import CustomLogger +import litellm +from fastapi import HTTPException + +# This file includes the custom callbacks for LiteLLM Proxy +# Once defined, these can be passed in proxy_config.yaml +class MyCustomHandler(CustomLogger): # https://docs.litellm.ai/docs/observability/custom_callback#callback-class + # Class variables or attributes + def __init__(self): + pass + + #### ASYNC #### + + async def async_log_stream_event(self, kwargs, response_obj, start_time, end_time): + pass + + async def async_log_pre_api_call(self, model, messages, kwargs): + pass + + async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): + pass + + async def async_log_failure_event(self, kwargs, response_obj, start_time, end_time): + pass + + #### CALL HOOKS - proxy only #### + + async def async_pre_call_hook(self, user_api_key_dict: UserAPIKeyAuth, cache: DualCache, data: dict, call_type: Literal["completion", "embeddings"]): + data["model"] = "my-new-model" + return data + + async def async_moderation_hook( ### 👈 KEY CHANGE ### + self, + data: dict, + ): + messages = data["messages"] + print(messages) + if messages[0]["content"] == "hello world": + raise HTTPException( + status_code=400, detail={"error": "Violated content safety policy"} + ) + +proxy_handler_instance = MyCustomHandler() +``` + + +2. Add this file to your proxy config + +```yaml +model_list: + - model_name: gpt-3.5-turbo + litellm_params: + model: gpt-3.5-turbo + +litellm_settings: + callbacks: custom_callbacks.proxy_handler_instance # sets litellm.callbacks = [proxy_handler_instance] +``` + +3. Start the server + test the request + +```shell +$ litellm /path/to/config.yaml +``` +```shell +curl --location 'http://0.0.0.0:8000/chat/completions' \ + --data ' { + "model": "gpt-3.5-turbo", + "messages": [ + { + "role": "user", + "content": "Hello world" + } + ], + }' +``` \ No newline at end of file diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index abdb4bc71..83b409969 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -138,7 +138,7 @@ const sidebars = { }, { "type": "category", - "label": "Admin Controls", + "label": "Content Moderation", "items": [ "proxy/call_hooks", "proxy/rules", From f35c340462351179c7f327b03dd204548e7d1bda Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 09:40:34 -0800 Subject: [PATCH 41/65] docs(call_hooks.md): add async_moderation_hook to docs --- docs/my-website/docs/proxy/call_hooks.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/my-website/docs/proxy/call_hooks.md b/docs/my-website/docs/proxy/call_hooks.md index 63d215764..b00f4e301 100644 --- a/docs/my-website/docs/proxy/call_hooks.md +++ b/docs/my-website/docs/proxy/call_hooks.md @@ -78,7 +78,7 @@ curl --location 'http://0.0.0.0:8000/chat/completions' \ ``` -## *NEW* async_moderation_hook +## [BETA] *NEW* async_moderation_hook Run a moderation check in parallel to the actual LLM API call. @@ -89,6 +89,12 @@ In your Custom Handler add a new `async_moderation_hook` function - If your `async_moderation_hook` raises an Exception, we will return that to the user. +:::info + +We might need to update the function schema in the future, to support multiple endpoints (e.g. accept a call_type). Please keep that in mind, while trying this feature + +::: + See a complete example with our [Llama Guard content moderation hook](https://github.com/BerriAI/litellm/blob/main/enterprise/hooks/llama_guard.py) ```python From dde0bab8273e51de0b9d40dd0ae5ed005a4ecde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Sat, 20 Jan 2024 16:09:40 +0100 Subject: [PATCH 42/65] nit: added urls to pyproject.toml This adds links to the various URLs from the PyPI page. --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index f44ad2fbb..98446b79b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,11 @@ authors = ["BerriAI"] license = "MIT" readme = "README.md" +[tool.poetry.urls] +homepage = "https://litellm.ai" +repository = "https://github.com/BerriAI/litellm" +documentation = "https://docs.litellm.ai" + [tool.poetry.dependencies] python = ">=3.8.1,<4.0, !=3.9.7" openai = ">=1.0.0" From 975974b61b8dac703efdeda69b3ad5e0c84cf933 Mon Sep 17 00:00:00 2001 From: Lunik Date: Fri, 16 Feb 2024 22:21:11 +0100 Subject: [PATCH 43/65] =?UTF-8?q?=E2=9C=A8=20Refresh=20Helm=20chart=20stru?= =?UTF-8?q?cture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lunik --- .gitignore | 4 +- .../litellm-helm/templates/deployment-ui.yaml | 89 ------------------- .../litellm-helm/templates/ingress-ui.yaml | 61 ------------- .../litellm-helm/templates/service-ui.yaml | 17 ---- .../{litellm-helm => litellm}/.helmignore | 0 .../{litellm-helm => litellm}/Chart.lock | 0 .../{litellm-helm => litellm}/Chart.yaml | 7 +- .../{litellm-helm => litellm}/README.md | 24 ++--- .../templates/NOTES.txt | 0 .../templates/_helpers.tpl | 12 --- .../templates/configmap-litellm.yaml | 0 .../templates/deployment.yaml} | 23 ++--- .../templates/hpa.yaml | 0 .../templates/ingress.yaml} | 2 +- .../templates/secret-dbcredentials.yaml | 2 +- .../templates/secret-masterkey.yaml | 2 +- .../templates/service.yaml} | 2 +- .../templates/serviceaccount.yaml | 0 .../templates/tests/test-connection.yaml | 2 +- .../{litellm-helm => litellm}/values.yaml | 69 ++------------ docs/my-website/docs/proxy/deploy.md | 29 ++++++ 21 files changed, 60 insertions(+), 285 deletions(-) delete mode 100644 deploy/charts/litellm-helm/templates/deployment-ui.yaml delete mode 100644 deploy/charts/litellm-helm/templates/ingress-ui.yaml delete mode 100644 deploy/charts/litellm-helm/templates/service-ui.yaml rename deploy/charts/{litellm-helm => litellm}/.helmignore (100%) rename deploy/charts/{litellm-helm => litellm}/Chart.lock (100%) rename deploy/charts/{litellm-helm => litellm}/Chart.yaml (96%) rename deploy/charts/{litellm-helm => litellm}/README.md (73%) rename deploy/charts/{litellm-helm => litellm}/templates/NOTES.txt (100%) rename deploy/charts/{litellm-helm => litellm}/templates/_helpers.tpl (79%) rename deploy/charts/{litellm-helm => litellm}/templates/configmap-litellm.yaml (100%) rename deploy/charts/{litellm-helm/templates/deployment-proxy.yaml => litellm/templates/deployment.yaml} (89%) rename deploy/charts/{litellm-helm => litellm}/templates/hpa.yaml (100%) rename deploy/charts/{litellm-helm/templates/ingress-proxy.yaml => litellm/templates/ingress.yaml} (96%) rename deploy/charts/{litellm-helm => litellm}/templates/secret-dbcredentials.yaml (87%) rename deploy/charts/{litellm-helm => litellm}/templates/secret-masterkey.yaml (75%) rename deploy/charts/{litellm-helm/templates/service-proxy.yaml => litellm/templates/service.yaml} (86%) rename deploy/charts/{litellm-helm => litellm}/templates/serviceaccount.yaml (100%) rename deploy/charts/{litellm-helm => litellm}/templates/tests/test-connection.yaml (92%) rename deploy/charts/{litellm-helm => litellm}/values.yaml (80%) diff --git a/.gitignore b/.gitignore index 00cd35c5b..de1c7598f 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,7 @@ ui/litellm-dashboard/node_modules ui/litellm-dashboard/next-env.d.ts ui/litellm-dashboard/package.json ui/litellm-dashboard/package-lock.json -deploy/charts/litellm-helm/*.tgz -deploy/charts/litellm-helm/charts/* +deploy/charts/litellm/*.tgz +deploy/charts/litellm/charts/* deploy/charts/*.tgz litellm/proxy/vertex_key.json diff --git a/deploy/charts/litellm-helm/templates/deployment-ui.yaml b/deploy/charts/litellm-helm/templates/deployment-ui.yaml deleted file mode 100644 index f949e2029..000000000 --- a/deploy/charts/litellm-helm/templates/deployment-ui.yaml +++ /dev/null @@ -1,89 +0,0 @@ -{{- if .Values.ui.enabled -}} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "litellm.fullname" . }}-ui - labels: - {{- include "litellm.labels" . | nindent 4 }} -spec: - {{- if not .Values.ui.autoscaling.enabled }} - replicas: {{ .Values.ui.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "litellm.ui.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "litellm.ui.labels" . | nindent 8 }} - {{- with .Values.ui.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "litellm.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.ui.podSecurityContext | nindent 8 }} - containers: - - name: {{ include "litellm.name" . }}-ui - securityContext: - {{- toYaml .Values.ui.securityContext | nindent 12 }} - image: "{{ .Values.ui.image.repository }}:{{ .Values.ui.image.tag | default (printf "main-%s" .Chart.AppVersion) }}" - imagePullPolicy: {{ .Values.ui.image.pullPolicy }} - env: - - name: BASE_URL - value: {{ (index .Values.ui.ingress.hosts 0).host | default "example.com" }} - ports: - - name: http - containerPort: {{ .Values.ui.service.port }} - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - # Give the container time to start up. Up to 5 minutes (10 * 30 seconds) - startupProbe: - httpGet: - path: / - port: http - failureThreshold: 30 - periodSeconds: 10 - resources: - {{- toYaml .Values.ui.resources | nindent 12 }} - volumeMounts: - - name: tmp - mountPath: /tmp - {{- with .Values.ui.volumeMounts }} - {{- toYaml . | nindent 12 }} - {{- end }} - volumes: - - name: tmp - emptyDir: - sizeLimit: 500Mi - {{- with .Values.ui.volumes }} - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.ui.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.ui.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.ui.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} -{{- end -}} \ No newline at end of file diff --git a/deploy/charts/litellm-helm/templates/ingress-ui.yaml b/deploy/charts/litellm-helm/templates/ingress-ui.yaml deleted file mode 100644 index 791ccf2b0..000000000 --- a/deploy/charts/litellm-helm/templates/ingress-ui.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if .Values.ui.ingress.enabled -}} -{{- $fullName := (printf "%s%s" (include "litellm.fullname" .) "-ui") -}} -{{- $svcPort := .Values.ui.service.port -}} -{{- if and .Values.ui.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ui.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ui.ingress.annotations "kubernetes.io/ingress.class" .Values.ui.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "litellm.ui.labels" . | nindent 4 }} - {{- with .Values.ui.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ui.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ui.ingress.className }} - {{- end }} - {{- if .Values.ui.ingress.tls }} - tls: - {{- range .Values.ui.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ui.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} diff --git a/deploy/charts/litellm-helm/templates/service-ui.yaml b/deploy/charts/litellm-helm/templates/service-ui.yaml deleted file mode 100644 index 50781899d..000000000 --- a/deploy/charts/litellm-helm/templates/service-ui.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.ui.enabled -}} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "litellm.fullname" . }}-ui - labels: - {{- include "litellm.labels" . | nindent 4 }} -spec: - type: {{ .Values.ui.service.type }} - ports: - - port: {{ .Values.ui.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "litellm.ui.selectorLabels" . | nindent 4 }} -{{ end -}} \ No newline at end of file diff --git a/deploy/charts/litellm-helm/.helmignore b/deploy/charts/litellm/.helmignore similarity index 100% rename from deploy/charts/litellm-helm/.helmignore rename to deploy/charts/litellm/.helmignore diff --git a/deploy/charts/litellm-helm/Chart.lock b/deploy/charts/litellm/Chart.lock similarity index 100% rename from deploy/charts/litellm-helm/Chart.lock rename to deploy/charts/litellm/Chart.lock diff --git a/deploy/charts/litellm-helm/Chart.yaml b/deploy/charts/litellm/Chart.yaml similarity index 96% rename from deploy/charts/litellm-helm/Chart.yaml rename to deploy/charts/litellm/Chart.yaml index 80eaf87dd..6ecdebb50 100644 --- a/deploy/charts/litellm-helm/Chart.yaml +++ b/deploy/charts/litellm/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 # We can't call ourselves just "litellm" because then we couldn't publish to the # same OCI repository as the "litellm" OCI image -name: litellm-helm +name: litellm description: Call all LLM APIs using the OpenAI format # A chart can be either an 'application' or a 'library' chart. @@ -18,17 +18,16 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.2.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: v1.18.9 +appVersion: v1.24.5 dependencies: - name: "postgresql" version: ">=13.3.0" repository: oci://registry-1.docker.io/bitnamicharts condition: db.deployStandalone - diff --git a/deploy/charts/litellm-helm/README.md b/deploy/charts/litellm/README.md similarity index 73% rename from deploy/charts/litellm-helm/README.md rename to deploy/charts/litellm/README.md index bf87501b3..daba8aa68 100644 --- a/deploy/charts/litellm-helm/README.md +++ b/deploy/charts/litellm/README.md @@ -43,20 +43,6 @@ data: type: Opaque ``` -### LiteLLM Admin UI Settings - -| Name | Description | Value | -| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| `ui.enabled` | Should the LiteLLM Admin UI be deployed | `true` | -| `ui.replicaCount` | The number of LiteLLM Admin UI pods to be deployed | `1` | -| `ui.image.repository` | LiteLLM Admin UI image repository | `ghcr.io/berriai/litellm` | -| `ui.image.pullPolicy` | LiteLLM Admin UI image pull policy | `IfNotPresent` | -| `ui.image.tag` | Overrides the image tag whose default the latest version of LiteLLM at the time this chart was published. | `""` | -| `ui.imagePullSecrets` | Registry credentials for the above images. | `[]` | -| `ui.service.type` | Kubernetes Service type (e.g. `LoadBalancer`, `ClusterIP`, etc.) | `ClusterIP` | -| `ui.service.port` | TCP port that the Kubernetes Service will listen on. Also the TCP port within the Pod that the web server will listen on. | `8000` | -| `ui.ingress.*` | See [values.yaml](./values.yaml) for example settings | N/A | - ### Database Settings | Name | Description | Value | | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | @@ -86,18 +72,18 @@ type: Opaque ``` ## Accessing the Admin UI -When browsing to the URL published per the settings in `ui.ingress.*`, you will +When browsing to the URL published per the settings in `ingress.*`, you will be prompted for **Admin Configuration**. The **Proxy Endpoint** is the internal -(from the `litellm-ui` pod's perspective) URL published by the `litellm-proxy` +(from the `litellm` pod's perspective) URL published by the `-litellm` Kubernetes Service. If the deployment uses the default settings for this -service, the **Proxy Endpoint** should be set to `http://litellm-proxy:8000`. +service, the **Proxy Endpoint** should be set to `http://-litellm:8000`. The **Proxy Key** is the value specified for `masterkey` or, if a `masterkey` was not provided to the helm command line, the `masterkey` is a randomly -generated string stored in the `litellm-masterkey` Kubernetes Secret. +generated string stored in the `-litellm-masterkey` Kubernetes Secret. ```bash -kubectl -n litellm get secret litellm-masterkey -o jsonpath="{.data.masterkey}" +kubectl -n litellm get secret -litellm-masterkey -o jsonpath="{.data.masterkey}" ``` ## Admin UI Limitations diff --git a/deploy/charts/litellm-helm/templates/NOTES.txt b/deploy/charts/litellm/templates/NOTES.txt similarity index 100% rename from deploy/charts/litellm-helm/templates/NOTES.txt rename to deploy/charts/litellm/templates/NOTES.txt diff --git a/deploy/charts/litellm-helm/templates/_helpers.tpl b/deploy/charts/litellm/templates/_helpers.tpl similarity index 79% rename from deploy/charts/litellm-helm/templates/_helpers.tpl rename to deploy/charts/litellm/templates/_helpers.tpl index 7e7aa8f4c..b8893d07c 100644 --- a/deploy/charts/litellm-helm/templates/_helpers.tpl +++ b/deploy/charts/litellm/templates/_helpers.tpl @@ -41,14 +41,6 @@ app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} -{{- define "litellm.ui.labels" -}} -helm.sh/chart: {{ include "litellm.chart" . }} -{{ include "litellm.ui.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} {{/* Selector labels @@ -57,10 +49,6 @@ Selector labels app.kubernetes.io/name: {{ include "litellm.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} -{{- define "litellm.ui.selectorLabels" -}} -app.kubernetes.io/name: {{ include "litellm.name" . }}-ui -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} {{/* Create the name of the service account to use diff --git a/deploy/charts/litellm-helm/templates/configmap-litellm.yaml b/deploy/charts/litellm/templates/configmap-litellm.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/configmap-litellm.yaml rename to deploy/charts/litellm/templates/configmap-litellm.yaml diff --git a/deploy/charts/litellm-helm/templates/deployment-proxy.yaml b/deploy/charts/litellm/templates/deployment.yaml similarity index 89% rename from deploy/charts/litellm-helm/templates/deployment-proxy.yaml rename to deploy/charts/litellm/templates/deployment.yaml index cdcd207c0..6ed112dac 100644 --- a/deploy/charts/litellm-helm/templates/deployment-proxy.yaml +++ b/deploy/charts/litellm/templates/deployment.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "litellm.fullname" . }}-proxy + name: {{ include "litellm.fullname" . }} labels: {{- include "litellm.labels" . | nindent 4 }} spec: @@ -41,12 +41,12 @@ spec: - name: DATABASE_USERNAME valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials key: username - name: PGPASSWORD valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials key: password - name: DATABASE_HOST value: {{ .Release.Name }}-postgresql @@ -108,12 +108,12 @@ spec: - name: DATABASE_USERNAME valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials key: username - name: DATABASE_PASSWORD valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials key: password - name: DATABASE_HOST value: {{ .Release.Name }}-postgresql @@ -140,7 +140,7 @@ spec: - name: PROXY_MASTER_KEY valueFrom: secretKeyRef: - name: {{ include "litellm.name" . }}-masterkey + name: {{ include "litellm.fullname" . }}-masterkey key: masterkey envFrom: {{- range .Values.environmentSecrets }} @@ -150,16 +150,7 @@ spec: args: - --config - /etc/litellm/config.yaml - # command: - # - bash - # - -c - # - | - # ls -la /etc/litellm/; cat /etc/litellm/config.yaml; export - # find / 2>/dev/null | grep -v -e '^/proc' -e '^/sys' -e '^/dev' >/tmp/before.list - # prisma generate - # find / 2>/dev/null | grep -v -e '^/proc' -e '^/sys' -e '^/dev' >/tmp/after.list - # diff -ruN /tmp/before.list /tmp/after.list - # sleep 3600 + - --run_gunicorn ports: - name: http containerPort: {{ .Values.service.port }} diff --git a/deploy/charts/litellm-helm/templates/hpa.yaml b/deploy/charts/litellm/templates/hpa.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/hpa.yaml rename to deploy/charts/litellm/templates/hpa.yaml diff --git a/deploy/charts/litellm-helm/templates/ingress-proxy.yaml b/deploy/charts/litellm/templates/ingress.yaml similarity index 96% rename from deploy/charts/litellm-helm/templates/ingress-proxy.yaml rename to deploy/charts/litellm/templates/ingress.yaml index 95bf83c99..09e8d715a 100644 --- a/deploy/charts/litellm-helm/templates/ingress-proxy.yaml +++ b/deploy/charts/litellm/templates/ingress.yaml @@ -1,5 +1,5 @@ {{- if .Values.ingress.enabled -}} -{{- $fullName := (printf "%s%s" (include "litellm.fullname" .) "-proxy") -}} +{{- $fullName := include "litellm.fullname" . -}} {{- $svcPort := .Values.service.port -}} {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} diff --git a/deploy/charts/litellm-helm/templates/secret-dbcredentials.yaml b/deploy/charts/litellm/templates/secret-dbcredentials.yaml similarity index 87% rename from deploy/charts/litellm-helm/templates/secret-dbcredentials.yaml rename to deploy/charts/litellm/templates/secret-dbcredentials.yaml index fc688effb..8851f5802 100644 --- a/deploy/charts/litellm-helm/templates/secret-dbcredentials.yaml +++ b/deploy/charts/litellm/templates/secret-dbcredentials.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Secret metadata: - name: {{ include "litellm.name" . }}-dbcredentials + name: {{ include "litellm.fullname" . }}-dbcredentials data: # Password for the "postgres" user postgres-password: {{ ( index .Values.postgresql.auth "postgres-password") | default "litellm" | b64enc }} diff --git a/deploy/charts/litellm-helm/templates/secret-masterkey.yaml b/deploy/charts/litellm/templates/secret-masterkey.yaml similarity index 75% rename from deploy/charts/litellm-helm/templates/secret-masterkey.yaml rename to deploy/charts/litellm/templates/secret-masterkey.yaml index 8b22b476c..57b854cc0 100644 --- a/deploy/charts/litellm-helm/templates/secret-masterkey.yaml +++ b/deploy/charts/litellm/templates/secret-masterkey.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Secret metadata: - name: {{ include "litellm.name" . }}-masterkey + name: {{ include "litellm.fullname" . }}-masterkey data: masterkey: {{ $masterkey | b64enc }} type: Opaque \ No newline at end of file diff --git a/deploy/charts/litellm-helm/templates/service-proxy.yaml b/deploy/charts/litellm/templates/service.yaml similarity index 86% rename from deploy/charts/litellm-helm/templates/service-proxy.yaml rename to deploy/charts/litellm/templates/service.yaml index 3c3c744b5..40e7f27f1 100644 --- a/deploy/charts/litellm-helm/templates/service-proxy.yaml +++ b/deploy/charts/litellm/templates/service.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "litellm.fullname" . }}-proxy + name: {{ include "litellm.fullname" . }} labels: {{- include "litellm.labels" . | nindent 4 }} spec: diff --git a/deploy/charts/litellm-helm/templates/serviceaccount.yaml b/deploy/charts/litellm/templates/serviceaccount.yaml similarity index 100% rename from deploy/charts/litellm-helm/templates/serviceaccount.yaml rename to deploy/charts/litellm/templates/serviceaccount.yaml diff --git a/deploy/charts/litellm-helm/templates/tests/test-connection.yaml b/deploy/charts/litellm/templates/tests/test-connection.yaml similarity index 92% rename from deploy/charts/litellm-helm/templates/tests/test-connection.yaml rename to deploy/charts/litellm/templates/tests/test-connection.yaml index 1f072069c..d2a4034b1 100644 --- a/deploy/charts/litellm-helm/templates/tests/test-connection.yaml +++ b/deploy/charts/litellm/templates/tests/test-connection.yaml @@ -11,5 +11,5 @@ spec: - name: wget image: busybox command: ['wget'] - args: ['{{ include "litellm.fullname" . }}:{{ .Values.service.port }}'] + args: ['{{ include "litellm.fullname" . }}:{{ .Values.service.port }}/health/readiness'] restartPolicy: Never diff --git a/deploy/charts/litellm-helm/values.yaml b/deploy/charts/litellm/values.yaml similarity index 80% rename from deploy/charts/litellm-helm/values.yaml rename to deploy/charts/litellm/values.yaml index 3c7131055..1b83fe801 100644 --- a/deploy/charts/litellm-helm/values.yaml +++ b/deploy/charts/litellm/values.yaml @@ -5,7 +5,9 @@ replicaCount: 1 image: - repository: ghcr.io/berriai/litellm + # Use "ghcr.io/berriai/litellm-database" for optimized image with database + # Alternatively, use "ghcr.io/berriai/litellm" for the default image + repository: ghcr.io/berriai/litellm-database pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. # tag: "main-latest" @@ -56,7 +58,7 @@ service: port: 8000 ingress: - enabled: true + enabled: false className: "nginx" annotations: {} # kubernetes.io/ingress.class: nginx @@ -71,6 +73,8 @@ ingress: # hosts: # - chart-example.local +# masterkey: changeit + # The elements within proxy_config are rendered as config.yaml for the proxy # Examples: https://github.com/BerriAI/litellm/tree/main/litellm/proxy/example_config_yaml # Reference: https://docs.litellm.ai/docs/proxy/configs @@ -159,61 +163,6 @@ postgresql: # A secret is created by this chart (litellm-helm) with the credentials that # the new Postgres instance should use. - existingSecret: litellm-dbcredentials - secretKeys: - userPasswordKey: password - -ui: - enabled: true - replicaCount: 1 - autoscaling: - enabled: false - image: - repository: ghcr.io/berriai/litellm-ui - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - # tag: "main-latest" - # TODO: Switch to BerryAI repo and tags if/when they provide a ui image - # https://github.com/BerriAI/litellm/pull/1505 - tag: "" - - service: - type: ClusterIP - port: 8501 - - ingress: - enabled: true - className: "nginx" - annotations: {} - hosts: - - host: ui.example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - - podAnnotations: {} - podLabels: {} - - podSecurityContext: - fsGroup: 1000 - - securityContext: - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - - resources: {} - - volumes: [] - - volumeMounts: [] - - nodeSelector: {} - - tolerations: [] - - affinity: {} \ No newline at end of file + # existingSecret: "" + # secretKeys: + # userPasswordKey: password diff --git a/docs/my-website/docs/proxy/deploy.md b/docs/my-website/docs/proxy/deploy.md index c3a84f1ea..f4a5dd8b2 100644 --- a/docs/my-website/docs/proxy/deploy.md +++ b/docs/my-website/docs/proxy/deploy.md @@ -151,6 +151,35 @@ kubectl port-forward service/litellm-service 4000:4000 Your OpenAI proxy server is now running on `http://0.0.0.0:4000`. + + + +### Step 1. Clone the repository + +```bash +git clone https://github.com/BerriAI/litellm.git +``` + +### Step 2. Deploy with Helm + +```bash +helm install \ + --set masterkey=SuPeRsEcReT \ + mydeploy \ + deploy/charts/litellm +``` + +### Step 3. Expose the service to localhost + +```bash +kubectl \ + port-forward \ + service/mydeploy-litellm \ + 8000:8000 +``` + +Your OpenAI proxy server is now running on `http://127.0.0.1:8000`. + From 9276e205bf7cf9cf1f9fa9b566cfbf5f8d82094c Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 13:03:24 -0800 Subject: [PATCH 44/65] (feat) set fast api root path --- litellm/proxy/proxy_server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 186ab0526..f95b25084 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -143,6 +143,9 @@ app = FastAPI( title="LiteLLM API", description=f"Proxy Server to call 100+ LLMs in the OpenAI format\n\n{ui_message}", version=version, + root_path=os.environ.get( + "SERVER_ROOT_PATH", "" + ), # check if user passed root path, FastAPI defaults this value to "" ) From a8f9a0f0b5aa2da7de9e9fdd13e0a34195c12e7c Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 13:12:06 -0800 Subject: [PATCH 45/65] (docs) setting server root path --- docs/my-website/docs/proxy/deploy.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/my-website/docs/proxy/deploy.md b/docs/my-website/docs/proxy/deploy.md index f4a5dd8b2..7ad7d8387 100644 --- a/docs/my-website/docs/proxy/deploy.md +++ b/docs/my-website/docs/proxy/deploy.md @@ -183,7 +183,22 @@ Your OpenAI proxy server is now running on `http://127.0.0.1:8000`. -## Setting SSL Certification +## Advanced Deployment Settings + +### Customization of the server root path + +:::info + +In a Kubernetes deployment, it's possible to utilize a shared DNS to host multiple applications by modifying the virtual service + +::: + +Customize the root path to eliminate the need for employing multiple DNS configurations during deployment. + +👉 Set `SERVER_ROOT_PATH` in your .env and this will be set as your server root path + + +### Setting SSL Certification Use this, If you need to set ssl certificates for your on prem litellm proxy From 614649659dfe6cf8d7d9323cdeb1112ad81a3a50 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 13:26:45 -0800 Subject: [PATCH 46/65] (fix) docs - incorrect title --- docs/my-website/sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index 83b409969..3badfc53a 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -129,7 +129,7 @@ const sidebars = { "proxy/caching", { "type": "category", - "label": "Logging, Alerting, Caching", + "label": "Logging, Alerting", "items": [ "proxy/logging", "proxy/alerting", From 8af1f5f9b0899425a162be67a5297ab773f200d3 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 13:57:42 -0800 Subject: [PATCH 47/65] (fix) ui - fix method not alowed error --- ui/litellm-dashboard/src/components/networking.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 7905ed7db..5874ae2a9 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -239,7 +239,7 @@ export const userSpendLogsCall = async ( export const keyInfoCall = async (accessToken: String, keys: String[]) => { try { - let url = proxyBaseUrl ? `${proxyBaseUrl}/v2/key/info` : `/key/info`; + let url = proxyBaseUrl ? `${proxyBaseUrl}/v2/key/info` : `/v2/key/info`; const response = await fetch(url, { method: "POST", From dceece00c9f69a78e33b96389d88df21615e7119 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 14:06:52 -0800 Subject: [PATCH 48/65] (fix) ui build --- litellm/proxy/_experimental/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/app/page-40ca7f9639d3a19d.js | 1 + .../out/_next/static/chunks/app/page-7bb820bd6902dbf2.js | 1 - litellm/proxy/_experimental/out/index.html | 2 +- litellm/proxy/_experimental/out/index.txt | 4 ++-- ui/litellm-dashboard/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/app/page-40ca7f9639d3a19d.js | 1 + .../out/_next/static/chunks/app/page-7bb820bd6902dbf2.js | 1 - ui/litellm-dashboard/out/index.html | 2 +- ui/litellm-dashboard/out/index.txt | 4 ++-- 14 files changed, 10 insertions(+), 10 deletions(-) rename litellm/proxy/_experimental/out/_next/static/{unBuvDqydg0yodtP5c3nQ => -A8U_xwfNq_YFjqXwnPm2}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{unBuvDqydg0yodtP5c3nQ => -A8U_xwfNq_YFjqXwnPm2}/_ssgManifest.js (100%) create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js rename ui/litellm-dashboard/out/_next/static/{unBuvDqydg0yodtP5c3nQ => -A8U_xwfNq_YFjqXwnPm2}/_buildManifest.js (100%) rename ui/litellm-dashboard/out/_next/static/{unBuvDqydg0yodtP5c3nQ => -A8U_xwfNq_YFjqXwnPm2}/_ssgManifest.js (100%) create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html index 03563194a..66998fa5b 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/unBuvDqydg0yodtP5c3nQ/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/unBuvDqydg0yodtP5c3nQ/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/unBuvDqydg0yodtP5c3nQ/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/unBuvDqydg0yodtP5c3nQ/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js new file mode 100644 index 000000000..cf687a562 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/app/page-7bb820bd6902dbf2.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js deleted file mode 100644 index 5402c8d1f..000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/index.html b/litellm/proxy/_experimental/out/index.html index 80296f4f6..775685e97 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 b6bb2c868..7751ad7e7 100644 --- a/litellm/proxy/_experimental/out/index.txt +++ b/litellm/proxy/_experimental/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-7bb820bd6902dbf2.js"],""] +3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-40ca7f9639d3a19d.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["unBuvDqydg0yodtP5c3nQ",[[["",{"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/c18941d97fb7245b.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["-A8U_xwfNq_YFjqXwnPm2",[[["",{"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/c18941d97fb7245b.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/out/404.html b/ui/litellm-dashboard/out/404.html index 03563194a..66998fa5b 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/unBuvDqydg0yodtP5c3nQ/_buildManifest.js b/ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/unBuvDqydg0yodtP5c3nQ/_buildManifest.js rename to ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/unBuvDqydg0yodtP5c3nQ/_ssgManifest.js b/ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/unBuvDqydg0yodtP5c3nQ/_ssgManifest.js rename to ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js new file mode 100644 index 000000000..cf687a562 --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/app/page-7bb820bd6902dbf2.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js deleted file mode 100644 index 5402c8d1f..000000000 --- a/ui/litellm-dashboard/out/_next/static/chunks/app/page-7bb820bd6902dbf2.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/index.html b/ui/litellm-dashboard/out/index.html index 80296f4f6..775685e97 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 b6bb2c868..7751ad7e7 100644 --- a/ui/litellm-dashboard/out/index.txt +++ b/ui/litellm-dashboard/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-7bb820bd6902dbf2.js"],""] +3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-40ca7f9639d3a19d.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["unBuvDqydg0yodtP5c3nQ",[[["",{"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/c18941d97fb7245b.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["-A8U_xwfNq_YFjqXwnPm2",[[["",{"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/c18941d97fb7245b.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 From 8f9d4c472634c3a606ea0f20f14729aa3c15ca20 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 14:10:27 -0800 Subject: [PATCH 49/65] =?UTF-8?q?bump:=20version=201.25.0=20=E2=86=92=201.?= =?UTF-8?q?25.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 98446b79b..49160f4a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.25.0" +version = "1.25.1" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -74,7 +74,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.25.0" +version = "1.25.1" version_files = [ "pyproject.toml:^version" ] From 9845d34075c829cff0fef28e91eda56eac49e966 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 16 Feb 2024 18:18:35 -0800 Subject: [PATCH 50/65] (fix) issue with storing model max budget --- litellm/proxy/_types.py | 2 ++ litellm/proxy/proxy_server.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index be2cdd6ef..5d74b9ded 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -421,6 +421,8 @@ class LiteLLM_UserTable(LiteLLMBase): user_id: str max_budget: Optional[float] spend: float = 0.0 + model_max_budget: Optional[Dict] = {} + model_spend: Optional[Dict] = {} user_email: Optional[str] models: list = [] diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index f95b25084..73d1da811 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -966,6 +966,7 @@ async def update_database( # track cost per model, for the given user spend_per_model = existing_spend_obj.model_spend or {} current_model = kwargs.get("model") + if current_model is not None and spend_per_model is not None: if spend_per_model.get(current_model) is None: spend_per_model[current_model] = response_cost @@ -979,7 +980,9 @@ async def update_database( key=id, value=existing_spend_obj.json() ) - verbose_proxy_logger.debug(f"new cost: {existing_spend_obj.spend}") + verbose_proxy_logger.debug( + f"user - new cost: {existing_spend_obj.spend}, user_id: {id}" + ) data_list.append(existing_spend_obj) # Update the cost column for the given user id From 28f20dd687c6869bba9afd02250ec5c975e5ad70 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 15:34:55 -0800 Subject: [PATCH 51/65] (docs) set budget per model --- docs/my-website/docs/proxy/virtual_keys.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/my-website/docs/proxy/virtual_keys.md b/docs/my-website/docs/proxy/virtual_keys.md index 2be4b95c1..83994701c 100644 --- a/docs/my-website/docs/proxy/virtual_keys.md +++ b/docs/my-website/docs/proxy/virtual_keys.md @@ -93,6 +93,7 @@ Request Params: - `config`: *Optional[dict]* - any key-specific configs, overrides config in config.yaml - `spend`: *Optional[int]* - Amount spent by key. Default is 0. Will be updated by proxy whenever key is used. https://docs.litellm.ai/docs/proxy/virtual_keys#managing-auth---tracking-spend - `max_budget`: *Optional[float]* - Specify max budget for a given key. +- `model_max_budget`: *Optional[dict[str, float]]* - Specify max budget for each model, `model_max_budget={"gpt4": 0.5, "gpt-5": 0.01}` - `max_parallel_requests`: *Optional[int]* - Rate limit a user based on the number of parallel requests. Raises 429 error, if user's parallel requests > x. - `metadata`: *Optional[dict]* - Metadata for key, store information for key. Example metadata = {"team": "core-infra", "app": "app2", "email": "ishaan@berri.ai" } @@ -676,8 +677,6 @@ general_settings: ### [BETA] Dynamo DB -Only live in `v1.16.21.dev1`. - #### Step 1. Save keys to env ```shell From db4fa90edb3aa0e827951f71fa9219436bd90b70 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 16:00:08 -0800 Subject: [PATCH 52/65] (fix) dynamo db test - new model_spend params --- litellm/proxy/db/dynamo_db.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/litellm/proxy/db/dynamo_db.py b/litellm/proxy/db/dynamo_db.py index 206fee777..08b365191 100644 --- a/litellm/proxy/db/dynamo_db.py +++ b/litellm/proxy/db/dynamo_db.py @@ -287,6 +287,8 @@ class DynamoDBWrapper(CustomDB): or k == "config" or k == "metadata" or k == "permissions" + or k == "model_spend" + or k == "model_max_budget" ) and v is not None and isinstance(v, str) From 07fb2cf741f4adec75517317eb20f9bda377ea1a Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Sat, 17 Feb 2024 16:45:46 -0800 Subject: [PATCH 53/65] Update README.md --- enterprise/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enterprise/README.md b/enterprise/README.md index 483ee1e44..12a271517 100644 --- a/enterprise/README.md +++ b/enterprise/README.md @@ -6,9 +6,9 @@ Code in this folder is licensed under a commercial license. Please review the [L 👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat?month=2024-02) -## Enterprise Features: +## Enterprise Features: [Docs](https://docs.litellm.ai/docs/proxy/enterprise) -- Track, View spend per tag https://docs.litellm.ai/docs/proxy/spend +- Track, View spend for custom tags - Custom API / microservice callbacks - Google Text Moderation API From 03da603f1f9acf7ec61aaf74e4f8b1cdd5de1ae3 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Sat, 17 Feb 2024 16:46:11 -0800 Subject: [PATCH 54/65] Update README.md --- enterprise/README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/enterprise/README.md b/enterprise/README.md index 12a271517..d5c27bab6 100644 --- a/enterprise/README.md +++ b/enterprise/README.md @@ -6,9 +6,4 @@ Code in this folder is licensed under a commercial license. Please review the [L 👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat?month=2024-02) -## Enterprise Features: [Docs](https://docs.litellm.ai/docs/proxy/enterprise) - -- Track, View spend for custom tags -- Custom API / microservice callbacks -- Google Text Moderation API - +See all Enterprise Features here 👉 [Docs](https://docs.litellm.ai/docs/proxy/enterprise) From 0400e954331ca44b03d1705611d4e5dbe4dd4670 Mon Sep 17 00:00:00 2001 From: Andres Barbaro Date: Fri, 16 Feb 2024 12:22:18 -0600 Subject: [PATCH 55/65] Add safety_settings parameter to gemini generate_content calls --- litellm/llms/gemini.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/litellm/llms/gemini.py b/litellm/llms/gemini.py index 7e98345b3..2db27aeba 100644 --- a/litellm/llms/gemini.py +++ b/litellm/llms/gemini.py @@ -121,6 +121,13 @@ def completion( ## Load Config inference_params = copy.deepcopy(optional_params) stream = inference_params.pop("stream", None) + + # Handle safety settings + safety_settings_param = inference_params.pop("safety_settings", None) + safety_settings = None + if safety_settings_param: + safety_settings = [genai.types.SafetySettingDict(x) for x in safety_settings_param] + config = litellm.GeminiConfig.get_config() for k, v in config.items(): if ( @@ -141,11 +148,13 @@ def completion( response = _model.generate_content( contents=prompt, generation_config=genai.types.GenerationConfig(**inference_params), + safety_settings=safety_settings, ) else: response = _model.generate_content( contents=prompt, generation_config=genai.types.GenerationConfig(**inference_params), + safety_settings=safety_settings, stream=True, ) return response From fcf2c2f1306541f070a434956d9b547c27a29e58 Mon Sep 17 00:00:00 2001 From: Andres Barbaro Date: Sat, 17 Feb 2024 17:31:37 -0600 Subject: [PATCH 56/65] Update gemini documentation --- docs/my-website/docs/providers/gemini.md | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/my-website/docs/providers/gemini.md b/docs/my-website/docs/providers/gemini.md index 9d5eb298b..44d744866 100644 --- a/docs/my-website/docs/providers/gemini.md +++ b/docs/my-website/docs/providers/gemini.md @@ -16,6 +16,34 @@ response = completion( ) ``` +## Specifying Safety Settings +In certain use-cases you may need to make calls to the models and pass [safety settigns](https://ai.google.dev/docs/safety_setting_gemini) different from the defaults. To do so, simple pass the `safety_settings` argument to `completion` or `acompletion`. For example: + +```python +response = completion( + model="gemini/gemini-pro", + messages=[{"role": "user", "content": "write code for saying hi from LiteLLM"}] + safety_settings=[ + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "BLOCK_NONE", + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "threshold": "BLOCK_NONE", + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "threshold": "BLOCK_NONE", + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "threshold": "BLOCK_NONE", + }, + ] +) +``` + # Gemini-Pro-Vision LiteLLM Supports the following image types passed in `url` - Images with direct links - https://storage.googleapis.com/github-repo/img/gemini/intro/landmark3.jpg From 5bb3d346bbedef4005a5962874668cc4bc4fa2b7 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 17:52:32 -0800 Subject: [PATCH 57/65] refactor: rename enterprise specific hooks and callbacks to be more precise --- .../example_logging_api.py | 0 .../generic_api_callback.py | 0 .../google_text_moderation.py | 0 .../{hooks => enterprise_hooks}/llama_guard.py | 0 litellm/proxy/proxy_server.py | 2 +- litellm/utils.py | 14 +++++--------- 6 files changed, 6 insertions(+), 10 deletions(-) rename enterprise/{callbacks => enterprise_callbacks}/example_logging_api.py (100%) rename enterprise/{callbacks => enterprise_callbacks}/generic_api_callback.py (100%) rename enterprise/{hooks => enterprise_hooks}/google_text_moderation.py (100%) rename enterprise/{hooks => enterprise_hooks}/llama_guard.py (100%) diff --git a/enterprise/callbacks/example_logging_api.py b/enterprise/enterprise_callbacks/example_logging_api.py similarity index 100% rename from enterprise/callbacks/example_logging_api.py rename to enterprise/enterprise_callbacks/example_logging_api.py diff --git a/enterprise/callbacks/generic_api_callback.py b/enterprise/enterprise_callbacks/generic_api_callback.py similarity index 100% rename from enterprise/callbacks/generic_api_callback.py rename to enterprise/enterprise_callbacks/generic_api_callback.py diff --git a/enterprise/hooks/google_text_moderation.py b/enterprise/enterprise_hooks/google_text_moderation.py similarity index 100% rename from enterprise/hooks/google_text_moderation.py rename to enterprise/enterprise_hooks/google_text_moderation.py diff --git a/enterprise/hooks/llama_guard.py b/enterprise/enterprise_hooks/llama_guard.py similarity index 100% rename from enterprise/hooks/llama_guard.py rename to enterprise/enterprise_hooks/llama_guard.py diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 73d1da811..0c6e01196 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1444,7 +1444,7 @@ class ProxyConfig: isinstance(callback, str) and callback == "llamaguard_moderations" ): - from litellm.proxy.enterprise.hooks.llama_guard import ( + from litellm.proxy.enterprise.enterprise_hooks.llama_guard import ( _ENTERPRISE_LlamaGuard, ) diff --git a/litellm/utils.py b/litellm/utils.py index d173b0092..faa464448 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -34,6 +34,7 @@ from dataclasses import ( try: # this works in python 3.8 import pkg_resources + filename = pkg_resources.resource_filename(__name__, "llms/tokenizers") # try: # filename = str( @@ -42,6 +43,7 @@ try: except: # this works in python 3.9+ from importlib import resources + filename = str( resources.files(litellm).joinpath("llms/tokenizers") # for python 3.10 ) # for python 3.10+ @@ -87,16 +89,10 @@ from .exceptions import ( UnprocessableEntityError, ) -# Import Enterprise features -project_path = abspath(join(dirname(__file__), "..", "..")) -# Add the "enterprise" directory to sys.path -verbose_logger.debug(f"current project_path: {project_path}") -enterprise_path = abspath(join(project_path, "enterprise")) -sys.path.append(enterprise_path) - -verbose_logger.debug(f"sys.path: {sys.path}") try: - from enterprise.callbacks.generic_api_callback import GenericAPILogger + from .proxy.enterprise.enterprise_callbacks.generic_api_callback import ( + GenericAPILogger, + ) except Exception as e: verbose_logger.debug(f"Exception import enterprise features {str(e)}") From fdf3cd4c59f66a291b331370de4b9c0ffb0eb01a Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 18:41:37 -0800 Subject: [PATCH 58/65] (fix) litellm bug in --- litellm/proxy/proxy_server.py | 32 ++++++++++++++++++-------------- litellm/proxy/utils.py | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 0c6e01196..073f8912e 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -93,6 +93,7 @@ from litellm.proxy.utils import ( html_form, _read_request_body, _is_valid_team_configs, + _is_user_proxy_admin, ) from litellm.proxy.secret_managers.google_kms import load_google_kms import pydantic @@ -503,11 +504,7 @@ async def user_api_key_auth( continue assert isinstance(_user, dict) # check if user is admin # - if ( - _user.get("user_role", None) is not None - and _user.get("user_role") == "proxy_admin" - ): - return UserAPIKeyAuth(api_key=master_key) + # Token exists, not expired now check if its in budget for the user user_max_budget = _user.get("max_budget", None) user_current_spend = _user.get("spend", None) @@ -642,11 +639,15 @@ async def user_api_key_auth( ) ) if ( - route.startswith("/key/") - or route.startswith("/user/") - or route.startswith("/model/") - or route.startswith("/spend/") - ) and (not is_master_key_valid): + ( + route.startswith("/key/") + or route.startswith("/user/") + or route.startswith("/model/") + or route.startswith("/spend/") + ) + and (not is_master_key_valid) + and (not _is_user_proxy_admin(user_id_information)) + ): allow_user_auth = False if ( general_settings.get("allow_user_auth", False) == True @@ -738,9 +739,12 @@ async def user_api_key_auth( # Do something if the current route starts with any of the allowed routes pass else: - raise Exception( - f"This key is made for LiteLLM UI, Tried to access route: {route}. Not allowed" - ) + if _is_user_proxy_admin(user_id_information): + pass + else: + raise Exception( + f"This key is made for LiteLLM UI, Tried to access route: {route}. Not allowed" + ) return UserAPIKeyAuth(api_key=api_key, **valid_token_dict) except Exception as e: # verbose_proxy_logger.debug(f"An exception occurred - {traceback.format_exc()}") @@ -4944,7 +4948,7 @@ async def auth_callback(request: Request): if user_id is None: user_id = getattr(result, "first_name", "") + getattr(result, "last_name", "") response = await generate_key_helper_fn( - **{"duration": "1hr", "key_max_budget": 0, "models": [], "aliases": {}, "config": {}, "spend": 0, "user_id": user_id, "team_id": "litellm-dashboard", "user_email": user_email} # type: ignore + **{"duration": "1hr", "key_max_budget": 0.01, "models": [], "aliases": {}, "config": {}, "spend": 0, "user_id": user_id, "team_id": "litellm-dashboard", "user_email": user_email} # type: ignore ) key = response["token"] # type: ignore user_id = response["user_id"] # type: ignore diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 7cc0f59f1..51e7c3c15 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1408,6 +1408,22 @@ def _is_valid_team_configs(team_id=None, team_config=None, request_data=None): return +def _is_user_proxy_admin(user_id_information=None): + if ( + user_id_information == None + or len(user_id_information) == 0 + or user_id_information[0] == None + ): + return False + _user = user_id_information[0] + if ( + _user.get("user_role", None) is not None + and _user.get("user_role") == "proxy_admin" + ): + return True + return False + + # LiteLLM Admin UI - Non SSO Login html_form = """ From 0febb44c5a9b9d39bb53deb2d3ec6111c6aae74e Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 19:03:32 -0800 Subject: [PATCH 59/65] fix(proxy_cli.py): allow user to control db connection pool + timeouts from config --- docs/my-website/docs/proxy/configs.md | 14 ++++++-------- litellm/proxy/_types.py | 7 +++++++ litellm/proxy/proxy_cli.py | 19 ++++++++++++++++--- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/docs/my-website/docs/proxy/configs.md b/docs/my-website/docs/proxy/configs.md index 786df59c2..e9fd4cda4 100644 --- a/docs/my-website/docs/proxy/configs.md +++ b/docs/my-website/docs/proxy/configs.md @@ -538,17 +538,13 @@ model_list: # will route requests to the least busy ollama model api_base: "http://127.0.0.1:8003" ``` -## Max Parallel Requests -To rate limit a user based on the number of parallel requests, e.g.: -if user's parallel requests > x, send a 429 error -if user's parallel requests <= x, let them use the API freely. - -set the max parallel request limit on the config.yaml (note: this expects the user to be passing in an api key). +## Configure DB Pool Limits + Connection Timeouts ```yaml -general_settings: - max_parallel_requests: 100 # max parallel requests for a user = 100 +general_settings: + database_connection_pool_limit: 100 # sets connection pool for prisma client to postgres db at 100 + database_connection_timeout: 60 # sets a 60s timeout for any connection call to the db ``` ## All settings @@ -577,6 +573,8 @@ general_settings: "key_management_system": "google_kms", # either google_kms or azure_kms "master_key": "string", "database_url": "string", + "database_connection_pool_limit": 0, # default 100 + "database_connection_timeout": 0, # default 60s "database_type": "dynamo_db", "database_args": { "billing_mode": "PROVISIONED_THROUGHPUT", diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 5d74b9ded..827a25a2b 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -311,6 +311,13 @@ class ConfigGeneralSettings(LiteLLMBase): None, description="connect to a postgres db - needed for generating temporary keys + tracking spend / key", ) + database_connection_pool_limit: Optional[int] = Field( + 100, + description="default connection pool for prisma client connecting to postgres db", + ) + database_connection_timeout: Optional[float] = Field( + 60, description="default timeout for a connection to the database" + ) database_type: Optional[Literal["dynamo_db"]] = Field( None, description="to use dynamodb instead of postgres db" ) diff --git a/litellm/proxy/proxy_cli.py b/litellm/proxy/proxy_cli.py index de21d0147..f6034cba3 100644 --- a/litellm/proxy/proxy_cli.py +++ b/litellm/proxy/proxy_cli.py @@ -409,6 +409,8 @@ def run_server( "uvicorn, gunicorn needs to be imported. Run - `pip install 'litellm[proxy]'`" ) + db_connection_pool_limit = 100 + db_connection_timeout = 60 if config is not None: """ Allow user to pass in db url via config @@ -427,6 +429,12 @@ def run_server( proxy_config.load_config(router=None, config_file_path=config) ) database_url = general_settings.get("database_url", None) + db_connection_pool_limit = general_settings.get( + "database_connection_pool_limit", 100 + ) + db_connection_timeout = general_settings.get( + "database_connection_timeout", 60 + ) if database_url and database_url.startswith("os.environ/"): original_dir = os.getcwd() # set the working directory to where this script is @@ -447,14 +455,19 @@ def run_server( try: if os.getenv("DATABASE_URL", None) is not None: ### add connection pool + pool timeout args - params = {"connection_limit": 100, "pool_timeout": 60} + params = { + "connection_limit": db_connection_pool_limit, + "pool_timeout": db_connection_timeout, + } database_url = os.getenv("DATABASE_URL") modified_url = append_query_params(database_url, params) os.environ["DATABASE_URL"] = modified_url - ### if os.getenv("DIRECT_URL", None) is not None: ### add connection pool + pool timeout args - params = {"connection_limit": 100, "pool_timeout": 60} + params = { + "connection_limit": db_connection_pool_limit, + "pool_timeout": db_connection_timeout, + } database_url = os.getenv("DIRECT_URL") modified_url = append_query_params(database_url, params) os.environ["DIRECT_URL"] = modified_url From 579677886a2cc99335651be5f5d5ce644cff9fa7 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 17 Feb 2024 11:04:56 -0800 Subject: [PATCH 60/65] feat(presidio_pii_masking.py): allow request level controls for turning on/off pii masking https://github.com/BerriAI/litellm/issues/2003 --- docs/my-website/docs/proxy/pii_masking.md | 75 +++++++++++++++++++++ litellm/proxy/hooks/presidio_pii_masking.py | 57 ++++++++++++++-- litellm/proxy/proxy_server.py | 13 ++++ 3 files changed, 141 insertions(+), 4 deletions(-) diff --git a/docs/my-website/docs/proxy/pii_masking.md b/docs/my-website/docs/proxy/pii_masking.md index 348ce207d..dafb5876c 100644 --- a/docs/my-website/docs/proxy/pii_masking.md +++ b/docs/my-website/docs/proxy/pii_masking.md @@ -72,3 +72,78 @@ curl --location 'http://0.0.0.0:8000/key/generate' \ ``` +## Turn on/off per request + +The proxy support 2 request-level PII controls: + +- *no-pii*: Optional(bool) - Allow user to turn off pii masking per request. +- *output_parse_pii*: Optional(bool) - Allow user to turn off pii output parsing per request. + +### Usage + +**Step 1. Create key with pii permissions** + +Set `allow_pii_controls` to true for a given key. This will allow the user to set request-level PII controls. + +```bash +curl --location 'http://0.0.0.0:8000/key/generate' \ +--header 'Authorization: Bearer my-master-key' \ +--header 'Content-Type: application/json' \ +--data '{ + "permissions": {"allow_pii_controls": true} +}' +``` + +**Step 2. Turn off pii output parsing** + +```python +import os +from openai import OpenAI + +client = OpenAI( + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), + base_url="http://0.0.0.0:8000" +) + +chat_completion = client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "My name is Jane Doe, my number is 8382043839", + } + ], + model="gpt-3.5-turbo", + extra_body={ + "content_safety": {"output_parse_pii": False} + } +) +``` + +**Step 3: See response** + +``` +{ + "id": "chatcmpl-8c5qbGTILZa1S4CK3b31yj5N40hFN", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Hi [PERSON], what can I help you with?", + "role": "assistant" + } + } + ], + "created": 1704089632, + "model": "gpt-35-turbo", + "object": "chat.completion", + "system_fingerprint": null, + "usage": { + "completion_tokens": 47, + "prompt_tokens": 12, + "total_tokens": 59 + }, + "_response_ms": 1753.426 +} +``` \ No newline at end of file diff --git a/litellm/proxy/hooks/presidio_pii_masking.py b/litellm/proxy/hooks/presidio_pii_masking.py index 6ea329613..031f4e2d4 100644 --- a/litellm/proxy/hooks/presidio_pii_masking.py +++ b/litellm/proxy/hooks/presidio_pii_masking.py @@ -119,6 +119,9 @@ class _OPTIONAL_PresidioPIIMasking(CustomLogger): call_type: str, ): """ + - Check if request turned off pii + - Check if user allowed to turn off pii (key permissions -> 'allow_pii_controls') + - Take the request data - Call /analyze -> get the results - Call /anonymize w/ the analyze results -> get the redacted text @@ -126,13 +129,59 @@ class _OPTIONAL_PresidioPIIMasking(CustomLogger): For multiple messages in /chat/completions, we'll need to call them in parallel. """ permissions = user_api_key_dict.permissions - - if permissions.get("pii", True) == False: # allow key to turn off pii masking - return data - output_parse_pii = permissions.get( "output_parse_pii", litellm.output_parse_pii ) # allow key to turn on/off output parsing for pii + no_pii = permissions.get( + "no-pii", None + ) # allow key to turn on/off pii masking (if user is allowed to set pii controls, then they can override the key defaults) + + if no_pii is None: + # check older way of turning on/off pii + no_pii = not permissions.get("pii", True) + + content_safety = data.get("content_safety", None) + verbose_proxy_logger.debug(f"content_safety: {content_safety}") + ## Request-level turn on/off PII controls ## + if content_safety is not None and isinstance(content_safety, dict): + # pii masking ## + if ( + content_safety.get("no-pii", None) is not None + and content_safety.get("no-pii") == True + ): + # check if user allowed to turn this off + if permissions.get("allow_pii_controls", False) == False: + raise HTTPException( + status_code=400, + detail={"error": "Not allowed to set PII controls per request"}, + ) + else: # user allowed to turn off pii masking + no_pii = content_safety.get("no-pii") + if not isinstance(no_pii, bool): + raise HTTPException( + status_code=400, + detail={"error": "no_pii needs to be a boolean value"}, + ) + ## pii output parsing ## + if content_safety.get("output_parse_pii", None) is not None: + # check if user allowed to turn this off + if permissions.get("allow_pii_controls", False) == False: + raise HTTPException( + status_code=400, + detail={"error": "Not allowed to set PII controls per request"}, + ) + else: # user allowed to turn on/off pii output parsing + output_parse_pii = content_safety.get("output_parse_pii") + if not isinstance(output_parse_pii, bool): + raise HTTPException( + status_code=400, + detail={ + "error": "output_parse_pii needs to be a boolean value" + }, + ) + + if no_pii == False: # turn off pii masking + return data if call_type == "completion": # /chat/completions requests messages = data["messages"] diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 073f8912e..295c80941 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -3156,6 +3156,19 @@ async def generate_key_fn( - permissions: Optional[dict] - key-specific permissions. Currently just used for turning off pii masking (if connected). Example - {"pii": false} - model_max_budget: Optional[dict] - key-specific model budget in USD. Example - {"text-davinci-002": 0.5, "gpt-3.5-turbo": 0.5}. IF null or {} then no model specific budget. + Examples: + + 1. Allow users to turn on/off pii masking + + ```bash + curl --location 'http://0.0.0.0:8000/key/generate' \ + --header 'Authorization: Bearer sk-1234' \ + --header 'Content-Type: application/json' \ + --data '{ + "permissions": {"allow_pii_controls": true} + }' + ``` + Returns: - key: (str) The generated api key - expires: (datetime) Datetime object for when key expires. From 1baa958923d760e1c8221250e377a4e96bd5f2a3 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 19:03:22 -0800 Subject: [PATCH 61/65] (fix) ui - reload key spend info --- .../src/components/user_dashboard.tsx | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ui/litellm-dashboard/src/components/user_dashboard.tsx b/ui/litellm-dashboard/src/components/user_dashboard.tsx index bf95d09f7..f87303a12 100644 --- a/ui/litellm-dashboard/src/components/user_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/user_dashboard.tsx @@ -48,6 +48,11 @@ const UserDashboard: React.FC = ({ const token = searchParams.get("token"); const [accessToken, setAccessToken] = useState(null); const [userModels, setUserModels] = useState([]); + window.addEventListener('beforeunload', function() { + // Clear session storage + sessionStorage.clear(); + }); + function formatUserRole(userRole: string) { if (!userRole) { @@ -70,6 +75,7 @@ const UserDashboard: React.FC = ({ // Moved useEffect inside the component and used a condition to run fetch only if the params are available useEffect(() => { + if (token) { const decoded = jwtDecode(token) as { [key: string]: any }; if (decoded) { @@ -97,22 +103,22 @@ const UserDashboard: React.FC = ({ } } if (userID && accessToken && userRole && !data) { - const cachedData = localStorage.getItem("userData" + userID); - const cachedSpendData = localStorage.getItem("userSpendData" + userID); - const cachedUserModels = localStorage.getItem("userModels" + userID); + const cachedData = sessionStorage.getItem("userData" + userID); + const cachedSpendData = sessionStorage.getItem("userSpendData" + userID); + const cachedUserModels = sessionStorage.getItem("userModels" + userID); if (cachedData && cachedSpendData && cachedUserModels) { setData(JSON.parse(cachedData)); setUserSpendData(JSON.parse(cachedSpendData)); setUserModels(JSON.parse(cachedUserModels)); - + } else { const fetchData = async () => { try { const response = await userInfoCall(accessToken, userID, userRole); setUserSpendData(response["user_info"]); setData(response["keys"]); // Assuming this is the correct path to your data - localStorage.setItem("userData" + userID, JSON.stringify(response["keys"])); - localStorage.setItem( + sessionStorage.setItem("userData" + userID, JSON.stringify(response["keys"])); + sessionStorage.setItem( "userSpendData" + userID, JSON.stringify(response["user_info"]) ); @@ -126,7 +132,7 @@ const UserDashboard: React.FC = ({ console.log("userModels:", userModels); - localStorage.setItem("userModels" + userID, JSON.stringify(available_model_names)); + sessionStorage.setItem("userModels" + userID, JSON.stringify(available_model_names)); From 5b318478c254b2f52f9f4ae6ca8c44c43e595a86 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 19:07:56 -0800 Subject: [PATCH 62/65] (build) new ui build --- litellm/proxy/_experimental/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/app/page-2322bcdc2ec71284.js | 1 + .../out/_next/static/chunks/app/page-40ca7f9639d3a19d.js | 1 - litellm/proxy/_experimental/out/index.html | 2 +- litellm/proxy/_experimental/out/index.txt | 4 ++-- ui/litellm-dashboard/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/app/page-2322bcdc2ec71284.js | 1 + .../out/_next/static/chunks/app/page-40ca7f9639d3a19d.js | 1 - ui/litellm-dashboard/out/index.html | 2 +- ui/litellm-dashboard/out/index.txt | 4 ++-- ui/litellm-dashboard/src/components/user_dashboard.tsx | 9 ++++++--- 15 files changed, 16 insertions(+), 13 deletions(-) rename litellm/proxy/_experimental/out/_next/static/{-A8U_xwfNq_YFjqXwnPm2 => S_8LZOnl2nyURq-NYnh2p}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{-A8U_xwfNq_YFjqXwnPm2 => S_8LZOnl2nyURq-NYnh2p}/_ssgManifest.js (100%) create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-2322bcdc2ec71284.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js rename ui/litellm-dashboard/out/_next/static/{-A8U_xwfNq_YFjqXwnPm2 => S_8LZOnl2nyURq-NYnh2p}/_buildManifest.js (100%) rename ui/litellm-dashboard/out/_next/static/{-A8U_xwfNq_YFjqXwnPm2 => S_8LZOnl2nyURq-NYnh2p}/_ssgManifest.js (100%) create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-2322bcdc2ec71284.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html index 66998fa5b..0a15886c7 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/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-2322bcdc2ec71284.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-2322bcdc2ec71284.js new file mode 100644 index 000000000..740eb87a2 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-2322bcdc2ec71284.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),L=n(67989),K=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(K.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(L.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(K.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=sessionStorage.getItem("userData"+t),s=sessionStorage.getItem("userSpendData"+t),l=sessionStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),sessionStorage.setItem("userData"+t,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),sessionStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/app/page-40ca7f9639d3a19d.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js deleted file mode 100644 index cf687a562..000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/index.html b/litellm/proxy/_experimental/out/index.html index 775685e97..a6994e708 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 7751ad7e7..2356232b4 100644 --- a/litellm/proxy/_experimental/out/index.txt +++ b/litellm/proxy/_experimental/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-40ca7f9639d3a19d.js"],""] +3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-2322bcdc2ec71284.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["-A8U_xwfNq_YFjqXwnPm2",[[["",{"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/c18941d97fb7245b.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["S_8LZOnl2nyURq-NYnh2p",[[["",{"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/c18941d97fb7245b.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/out/404.html b/ui/litellm-dashboard/out/404.html index 66998fa5b..0a15886c7 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/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js b/ui/litellm-dashboard/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_buildManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_buildManifest.js rename to ui/litellm-dashboard/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_buildManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js b/ui/litellm-dashboard/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_ssgManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/-A8U_xwfNq_YFjqXwnPm2/_ssgManifest.js rename to ui/litellm-dashboard/out/_next/static/S_8LZOnl2nyURq-NYnh2p/_ssgManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/chunks/app/page-2322bcdc2ec71284.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-2322bcdc2ec71284.js new file mode 100644 index 000000000..740eb87a2 --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/chunks/app/page-2322bcdc2ec71284.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),L=n(67989),K=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(K.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(L.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(K.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=sessionStorage.getItem("userData"+t),s=sessionStorage.getItem("userSpendData"+t),l=sessionStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),sessionStorage.setItem("userData"+t,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),sessionStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/app/page-40ca7f9639d3a19d.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js deleted file mode 100644 index cf687a562..000000000 --- a/ui/litellm-dashboard/out/_next/static/chunks/app/page-40ca7f9639d3a19d.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,t,n){Promise.resolve().then(n.bind(n,48016))},48016:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return ec}});var s=n(3827),l=n(64090),r=n(47907),a=n(8792),o=n(2179),i=e=>{let{userID:t,userRole:n,userEmail:l}=e;return console.log("User ID:",t),console.log("userEmail:",l),(0,s.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,s.jsx)("div",{className:"text-left mx-4 my-2 absolute top-0 left-0",children:(0,s.jsx)("div",{className:"flex flex-col items-center",children:(0,s.jsx)(a.default,{href:"/",children:(0,s.jsx)("button",{className:"text-gray-800 text-2xl px-4 py-1 rounded text-center",children:"\uD83D\uDE85 LiteLLM"})})})}),(0,s.jsx)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0",children:(0,s.jsxs)(o.Z,{variant:"secondary",children:[l,(0,s.jsxs)("p",{children:["Role: ",n]}),(0,s.jsxs)("p",{children:["ID: ",t]})]})})]})},c=n(80588);let d=async(e,t,n)=>{try{if(console.log("Form Values in keyCreateCall:",n),n.description&&(n.metadata||(n.metadata={}),n.metadata.description=n.description,delete n.description,n.metadata=JSON.stringify(n.metadata)),n.metadata){console.log("formValues.metadata:",n.metadata);try{n.metadata=JSON.parse(n.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",n);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...n})});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}},m=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let n=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let s=await n.json();return console.log(s),c.ZP.success("API Key Deleted"),s}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,n)=>{try{let s="/user/info";"App Owner"==n&&(s="".concat(s,"/?user_id=").concat(t)),c.ZP.info("Requesting user data");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(e),Error("Network response was not ok")}let r=await l.json();return c.ZP.info("Received user data"),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t,n)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/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")}let n=await t.json();return c.ZP.info("Received model data"),n}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{let n="/spend/logs";console.log("in keySpendLogsCall:",n);let s=await fetch("".concat(n,"/?api_key=").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}},p=async(e,t,n,s,l,r)=>{try{let t="/spend/logs";t="App Owner"==n?"".concat(t,"/?user_id=").concat(s,"&start_date=").concat(l,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(l,"&end_date=").concat(r),c.ZP.info("Making spend logs request");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),c.ZP.success("Spend Logs received"),o}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let n=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!n.ok){let e=await n.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let n="/spend/users";console.log("in spendUsersCall:",n);let s=await fetch("".concat(n,"/?user_id=").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 get spend for user",e),e}};var y=n(10384),f=n(46453),Z=n(71801),w=n(13969),k=n(12143),_=n(77171),v=n(29714),b=n(88707),S=n(1861);let{Option:N}=w.default;var I=e=>{let{userID:t,userRole:n,accessToken:r,data:a,userModels:i,setData:m}=e,[h]=k.Z.useForm(),[u,x]=(0,l.useState)(!1),[p,j]=(0,l.useState)(null),g=()=>{x(!1),h.resetFields()},I=()=>{x(!1),j(null),h.resetFields()},A=async e=>{try{c.ZP.info("Making API Call"),x(!0);let n=await d(r,t,e);m(e=>e?[...e,n]:[n]),j(n.key),c.ZP.success("API Key Created"),h.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>x(!0),children:"+ Create New Key"}),(0,s.jsx)(_.Z,{title:"Create Key",visible:u,width:800,footer:null,onOk:g,onCancel:I,children:(0,s.jsxs)(k.Z,{form:h,onFinish:A,labelCol:{span:6},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===n||"Admin"===n?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,s.jsx)(w.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:i.map(e=>(0,s.jsx)(N,{value:e,children:e},e))})}),(0,s.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,s.jsx)(b.Z,{step:.01,precision:2,width:200})}),(0,s.jsx)(k.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,s.jsx)(v.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(k.Z.Item,{label:"Key Name",name:"key_alias",children:(0,s.jsx)(v.Z,{})}),(0,s.jsx)(k.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,s.jsx)(v.Z,{placeholder:"ai_team"})}),(0,s.jsx)(k.Z.Item,{label:"Description",name:"description",children:(0,s.jsx)(v.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,s.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,s.jsx)(S.ZP,{htmlType:"submit",children:"Create Key"})})]})}),p&&(0,s.jsx)(_.Z,{title:"Save your key",visible:u,onOk:g,onCancel:I,footer:null,children:(0,s.jsxs)(f.Z,{numItems:1,className:"gap-2 w-full",children:[(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,s.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,s.jsx)(y.Z,{numColSpan:1,children:null!=p?(0,s.jsxs)(Z.Z,{children:["API Key: ",p]}):(0,s.jsx)(Z.Z,{children:"Key being created, this might take 30s"})})]})})]})},A=n(33393),C=n(13810),D=n(61244),T=n(10827),P=n(3851),E=n(2044),O=n(64167),R=n(74480),M=n(7178),U=n(42440),F=n(9853),K=n(67989),L=n(56863),z=e=>{let{token:t,accessToken:n,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,l.useState)(!1),[m,h]=(0,l.useState)(null),[u,p]=(0,l.useState)(null),j=async()=>{try{if(null==n||null==t)return;console.log("accessToken: ".concat(n,"; token: ").concat(t));let e=await x(n,t);console.log("Response:",e);let s=Object.values(e).reduce((e,t)=>{let n=new Date(t.startTime),s=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(n);return e[s]=(e[s]||0)+t.spend,e},{}),l=Object.entries(s);l.sort((e,t)=>{let[n]=e,[s]=t,l=new Date(n),r=new Date(s);return l.getTime()-r.getTime()});let r=Object.fromEntries(l);console.log(r);let a=Object.values(e).reduce((e,t)=>{let n=t.user;return e[n]=(e[n]||0)+t.spend,e},{});console.log(s),console.log(a);let o=[];for(let[e,t]of Object.entries(r))o.push({day:e,spend:t});let i=Object.entries(a).sort((e,t)=>t[1]-e[1]).slice(0,5).map(e=>{let[t,n]=e;return{name:t,value:n}});h(o),p(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,s.jsxs)("div",{children:[(0,s.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),j()},children:"View Spend Report"}),(0,s.jsxs)(_.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,s.jsxs)(U.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,s.jsxs)(L.Z,{children:["Monthly Spend $",r]}),(0,s.jsx)(C.Z,{className:"mt-6 mb-6",children:m&&(0,s.jsx)(F.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,s.jsx)(U.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,s.jsx)(C.Z,{className:"mb-6",children:u&&(0,s.jsx)(K.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},B=e=>{let{userID:t,accessToken:n,data:r,setData:a}=e,[i,c]=(0,l.useState)(!1),[d,h]=(0,l.useState)(!1),[u,x]=(0,l.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),h(!0))},j=async()=>{if(null!=u&&null!=r){try{await m(n,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}h(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,s.jsxs)(C.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,s.jsx)(U.Z,{children:"API Keys"}),(0,s.jsxs)(T.Z,{className:"mt-5",children:[(0,s.jsx)(O.Z,{children:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(R.Z,{children:"Key Alias"}),(0,s.jsx)(R.Z,{children:"Secret Key"}),(0,s.jsx)(R.Z,{children:"Spend (USD)"}),(0,s.jsx)(R.Z,{children:"Key Budget (USD)"}),(0,s.jsx)(R.Z,{children:"Team ID"}),(0,s.jsx)(R.Z,{children:"Metadata"}),(0,s.jsx)(R.Z,{children:"Models"}),(0,s.jsx)(R.Z,{children:"TPM / RPM Limits"}),(0,s.jsx)(R.Z,{children:"Expires"})]})}),(0,s.jsx)(P.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:null!=e.key_alias?(0,s.jsx)(Z.Z,{children:e.key_alias}):(0,s.jsx)(Z.Z,{children:"Not Set"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.key_name})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.spend})}),(0,s.jsx)(E.Z,{children:null!=e.max_budget?(0,s.jsx)(Z.Z,{children:e.max_budget}):(0,s.jsx)(Z.Z,{children:"Unlimited Budget"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:e.team_id})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.metadata)})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(Z.Z,{children:JSON.stringify(e.models)})}),(0,s.jsx)(E.Z,{children:(0,s.jsxs)(Z.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,s.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,s.jsx)(E.Z,{children:null!=e.expires?(0,s.jsx)(Z.Z,{children:e.expires}):(0,s.jsx)(Z.Z,{children:"Never expires"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(D.Z,{onClick:()=>p(e.token),icon:A.Z,size:"sm"})}),(0,s.jsx)(E.Z,{children:(0,s.jsx)(z,{token:e.token,accessToken:n,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,s.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,s.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,s.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,s.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,s.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,s.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,s.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,s.jsx)("div",{className:"sm:flex sm:items-start",children:(0,s.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,s.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,s.jsx)("div",{className:"mt-2",children:(0,s.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,s.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,s.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,s.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},J=e=>{let{userID:t,userSpendData:n,userRole:r,accessToken:a}=e;console.log("User SpendData:",n);let[o,i]=(0,l.useState)(null==n?void 0:n.spend),[c,d]=(0,l.useState)((null==n?void 0:n.max_budget)||null);return(0,l.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await g(a,"litellm-proxy-budget");console.log("Result from callSpendUsers:",e);let t=e[0];i(null==t?void 0:t.spend),d((null==t?void 0:t.max_budget)||null)}catch(e){console.error("Failed to get spend for user",e)}})()},[r,a,t]),(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(C.Z,{className:"mx-auto mb-4",children:[(0,s.jsxs)(L.Z,{children:["$",o]}),(0,s.jsxs)(U.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},q=n(37963);console.log("isLocal:",!1);var G=e=>{let{userID:t,userRole:n,setUserRole:a,userEmail:o,setUserEmail:i}=e,[c,d]=(0,l.useState)(null),[m,x]=(0,l.useState)(null),p=(0,r.useSearchParams)();p.get("viewSpend"),(0,r.useRouter)();let j=p.get("token"),[g,Z]=(0,l.useState)(null),[w,k]=(0,l.useState)([]);if((0,l.useEffect)(()=>{if(j){let e=(0,q.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),a(t)}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&g&&n&&!c){let e=localStorage.getItem("userData"+t),s=localStorage.getItem("userSpendData"+t),l=localStorage.getItem("userModels"+t);e&&s&&l?(d(JSON.parse(e)),x(JSON.parse(s)),k(JSON.parse(l))):(async()=>{try{let e=await h(g,t,n);x(e.user_info),d(e.keys),localStorage.setItem("userData"+t,JSON.stringify(e.keys)),localStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let s=await u(g,t,n);console.log("model_info:",s);let l=s.data.map(e=>e.model_name);console.log("available_model_names:",l),k(l),console.log("userModels:",w),localStorage.setItem("userModels"+t,JSON.stringify(l))}catch(e){console.error("There was an error fetching the data",e)}})()}},[t,j,g,c,n]),null==t||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==g?null:(null==n&&a("App Owner"),(0,s.jsx)("div",{children:(0,s.jsx)(f.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,s.jsxs)(y.Z,{numColSpan:1,children:[(0,s.jsx)(J,{userID:t,userSpendData:m,userRole:n,accessToken:g}),(0,s.jsx)(B,{userID:t,accessToken:g,data:c,setData:d}),(0,s.jsx)(I,{userID:t,userRole:n,userModels:w,accessToken:g,data:c,setData:d})]})})}))},$=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)({data:[]});if((0,l.useEffect)(()=>{if(!t||!n||!r||!a)return;let e=async()=>{try{let e=await u(t,a,r);console.log("Model data response:",e.data),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&n&&r&&a&&e()},[t,n,r,a]),!o)return(0,s.jsx)("div",{children:"Loading..."});for(let e=0;e(0,s.jsxs)(M.Z,{children:[(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:e.model_name})}),(0,s.jsx)(E.Z,{children:e.provider}),(0,s.jsx)(E.Z,{children:e.input_cost}),(0,s.jsx)(E.Z,{children:e.output_cost}),(0,s.jsx)(E.Z,{children:e.max_tokens})]},e.model_name))})]})})})})},V=n(92836),W=n(26734),H=n(41608),Y=n(32126),X=n(23682),Q=n(12968),ee=n(67951);async function et(e,t,n,s){console.log("isLocal:",!1);let l=window.location.origin,r=new Q.ZP.OpenAI({apiKey:s,baseURL:l,dangerouslyAllowBrowser:!0});for await(let s of(await r.chat.completions.create({model:n,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&t(s.choices[0].delta.content)}var en=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,[o,i]=(0,l.useState)(""),[c,d]=(0,l.useState)([]),[m,h]=(0,l.useState)(void 0),[x,p]=(0,l.useState)(null);(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{let e=await u(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(p(e),h(e.data[0].model_name))})()},[t,a,r]);let j=(e,t)=>{d(n=>{let s=n[n.length-1];return s&&s.role===e?[...n.slice(0,n.length-1),{role:e,content:s.content+t}]:[...n,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&n&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await et(o,e=>j("assistant",e),m,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,s.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,s.jsx)(f.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,s.jsx)(C.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{className:"mt-4",children:[(0,s.jsx)(V.Z,{children:"Chat"}),(0,s.jsx)(V.Z,{children:"API Reference"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsxs)(Y.Z,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{children:"Select Model:"}),(0,s.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==x?void 0:x.data.map(e=>(0,s.jsx)("option",{value:e.model_name,children:e.model_name},e.model_name))})]}),(0,s.jsxs)(T.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,s.jsx)(O.Z,{children:(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:(0,s.jsx)(U.Z,{children:"Chat"})})})}),(0,s.jsx)(P.Z,{children:c.map((e,t)=>(0,s.jsx)(M.Z,{children:(0,s.jsx)(E.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,s.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,s.jsxs)("div",{className:"flex",children:[(0,s.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,s.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,s.jsx)(Y.Z,{children:(0,s.jsxs)(W.Z,{children:[(0,s.jsxs)(H.Z,{children:[(0,s.jsx)(V.Z,{children:"OpenAI Python SDK"}),(0,s.jsx)(V.Z,{children:"LlamaIndex"}),(0,s.jsx)(V.Z,{children:"Langchain Py"})]}),(0,s.jsxs)(X.Z,{children:[(0,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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,s.jsx)(Y.Z,{children:(0,s.jsx)(ee.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 '})})]})]})})]})]})})})})},es=n(33509),el=n(30569);let{Sider:er}=es.default;var ea=e=>{let{setPage:t}=e;return(0,s.jsx)(es.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,s.jsx)(er,{width:120,children:(0,s.jsxs)(el.Z,{mode:"inline",defaultSelectedKeys:["1"],style:{height:"100%",borderRight:0},children:[(0,s.jsx)(el.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,s.jsx)(el.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4")]})})})};let eo=e=>{let{payload:t,active:n}=e;if(!n||!t)return null;let l=t[0].payload,r=l.startTime,a=Object.entries(l.models).map(e=>{let[t,n]=e;return[t,n]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,s.jsxs)("div",{className:"w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r,o.map(e=>{let[t,n]=e;return(0,s.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,s.jsx)("div",{className:"p-2",children:(0,s.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,s.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",n?n<.01?"<$0.01":n.toFixed(2):""]})]})})},t)})]})};var ei=e=>{let{accessToken:t,token:n,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)([]),[h,u]=(0,l.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),g=new Date(o.getFullYear(),o.getMonth()+1,0),Z=k(x),w=k(g);function k(e){let t=e.getFullYear(),n=e.getMonth()+1,s=e.getDate();return"".concat(t,"-").concat(n<10?"0"+n:n,"-").concat(s<10?"0"+s:s)}return console.log("Start date is ".concat(Z)),console.log("End date is ".concat(w)),(0,l.useEffect)(()=>{t&&n&&r&&a&&(async()=>{try{await p(t,n,r,a,Z,w).then(async e=>{let n=(await j(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[n,s]=e;"spend"!==n&&"startTime"!==n&&"models"!==n&&"users"!==n&&t.push({key:n,spend:s})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let n=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(n[0]))),n}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(n),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[n,s]=e;""!==n&&null!=n&&"None"!=n&&(t[n]||(t[n]=0),t[n]+=s)})});let n=Object.entries(t).map(e=>{let[t,n]=e;return{user_id:t,spend:n}});n.sort((e,t)=>t.spend-e.spend);let s=n.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,n,r,a,Z,w]),(0,s.jsx)("div",{style:{width:"100%"},children:(0,s.jsxs)(f.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,s.jsx)(y.Z,{numColSpan:2,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Monthly Spend"}),(0,s.jsx)(F.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:eo})]})}),(0,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top API Keys"}),(0,s.jsx)(F.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,s.jsx)(y.Z,{numColSpan:1,children:(0,s.jsxs)(C.Z,{children:[(0,s.jsx)(U.Z,{children:"Top Users"}),(0,s.jsx)(F.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},ec=()=>{let[e,t]=(0,l.useState)(null),[n,a]=(0,l.useState)(null),o=(0,r.useSearchParams)(),c=o.get("userID"),d=o.get("token"),[m,h]=(0,l.useState)("api-keys"),[u,x]=(0,l.useState)(null);return(0,l.useEffect)(()=>{if(d){let e=(0,q.o)(d);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),x(e.key),e.user_role){let n=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":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",n),t(n)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e))}}},[d]),(0,s.jsx)(l.Suspense,{fallback:(0,s.jsx)("div",{children:"Loading..."}),children:(0,s.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,s.jsx)(i,{userID:c,userRole:e,userEmail:n}),(0,s.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,s.jsx)(ea,{setPage:h}),"api-keys"==m?(0,s.jsx)(G,{userID:c,userRole:e,setUserRole:t,userEmail:n,setUserEmail:a}):"models"==m?(0,s.jsx)($,{userID:c,userRole:e,token:d,accessToken:u}):"llm-playground"==m?(0,s.jsx)(en,{userID:c,userRole:e,token:d,accessToken:u}):(0,s.jsx)(ei,{userID:c,userRole:e,token:d,accessToken:u})]})]})})}}},function(e){e.O(0,[145,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/index.html b/ui/litellm-dashboard/out/index.html index 775685e97..a6994e708 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 7751ad7e7..2356232b4 100644 --- a/ui/litellm-dashboard/out/index.txt +++ b/ui/litellm-dashboard/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-40ca7f9639d3a19d.js"],""] +3:I[48016,["145","static/chunks/145-9c160ad5539e000f.js","931","static/chunks/app/page-2322bcdc2ec71284.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["-A8U_xwfNq_YFjqXwnPm2",[[["",{"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/c18941d97fb7245b.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["S_8LZOnl2nyURq-NYnh2p",[[["",{"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/c18941d97fb7245b.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/user_dashboard.tsx b/ui/litellm-dashboard/src/components/user_dashboard.tsx index f87303a12..f3f24e444 100644 --- a/ui/litellm-dashboard/src/components/user_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/user_dashboard.tsx @@ -48,11 +48,14 @@ const UserDashboard: React.FC = ({ const token = searchParams.get("token"); const [accessToken, setAccessToken] = useState(null); const [userModels, setUserModels] = useState([]); - window.addEventListener('beforeunload', function() { + + // check if window is not undefined + if (typeof window !== "undefined") { + window.addEventListener('beforeunload', function() { // Clear session storage sessionStorage.clear(); - }); - + }); + } function formatUserRole(userRole: string) { if (!userRole) { From 2927811c1944b45a21bb7e063c6b2c14db053e6a Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 19:17:33 -0800 Subject: [PATCH 63/65] (fix) testing fix --- litellm/proxy/utils.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 51e7c3c15..1cdacf76f 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -1379,19 +1379,22 @@ async def _read_request_body(request): """ import ast, json - request_data = {} - if request is None: - return request_data - body = await request.body() - - if body == b"" or body is None: - return request_data - body_str = body.decode() try: - request_data = ast.literal_eval(body_str) + request_data = {} + if request is None: + return request_data + body = await request.body() + + if body == b"" or body is None: + return request_data + body_str = body.decode() + try: + request_data = ast.literal_eval(body_str) + except: + request_data = json.loads(body_str) + return request_data except: - request_data = json.loads(body_str) - return request_data + return {} def _is_valid_team_configs(team_id=None, team_config=None, request_data=None): From 55b5b5d3b4ef6c3eb701ff9198f066ef9aa56745 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 19:22:55 -0800 Subject: [PATCH 64/65] =?UTF-8?q?bump:=20version=201.25.1=20=E2=86=92=201.?= =?UTF-8?q?25.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 49160f4a9..442c8ed04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.25.1" +version = "1.25.2" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -74,7 +74,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.25.1" +version = "1.25.2" version_files = [ "pyproject.toml:^version" ] From fff2ec08a47a8371f776fbd6d1275184faafb3c6 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Sat, 17 Feb 2024 19:23:40 -0800 Subject: [PATCH 65/65] (ci/cd) run again --- litellm/tests/test_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index 475364bfa..f2186093a 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -1912,7 +1912,7 @@ def test_mistral_anyscale_stream(): # test_baseten_wizardLMcompletion_withbase() # def test_baseten_mosaic_ML_completion_withbase(): -# model_name = "31dxrj3" +# model_name = "31dxrj3", # litellm.api_base = "https://app.baseten.co" # try: # response = completion(model=model_name, messages=messages)