From 6a94ef6c16f9ea57ef60a1e3899e46082f331e25 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 12:47:51 -0800 Subject: [PATCH 01/12] fix(proxy_server.py): introduces a beta endpoint for admin to view global spend --- litellm/proxy/proxy_server.py | 24 +++- litellm/tests/test_key_generate_prisma.py | 104 ++++++++++++++++++ .../src/components/networking.tsx | 35 ++++++ ui/litellm-dashboard/src/components/usage.tsx | 39 ++++--- 4 files changed, 183 insertions(+), 19 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 2e9a48460cd..c887351ef2c 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -3788,7 +3788,7 @@ async def view_spend_tags( @router.get( "/spend/logs", - tags=["budget & spend Tracking"], + tags=["Budget & Spend Tracking"], dependencies=[Depends(user_api_key_auth)], responses={ 200: {"model": List[LiteLLM_SpendLogs]}, @@ -4048,6 +4048,28 @@ async def view_spend_logs( ) +@router.get( + "/global/spend/logs", + tags=["Budget & Spend Tracking"], + dependencies=[Depends(user_api_key_auth)], +) +async def global_spend_logs(): + """ + [BETA] This is a beta endpoint. + + Use this to get global spend (spend per day for last 30d). Admin-only endpoint + + More efficient implementation of /spend/logs, by creating a view over the spend logs table. + """ + global prisma_client + + sql_query = """SELECT * FROM "globalspendperdate";""" + + response = await prisma_client.db.query_raw(query=sql_query) + + return response + + @router.get( "/daily_metrics", summary="Get daily spend metrics", diff --git a/litellm/tests/test_key_generate_prisma.py b/litellm/tests/test_key_generate_prisma.py index 3660d6371c8..91f34e79100 100644 --- a/litellm/tests/test_key_generate_prisma.py +++ b/litellm/tests/test_key_generate_prisma.py @@ -80,6 +80,14 @@ request_data = { @pytest.fixture def prisma_client(): + from litellm.proxy.proxy_cli import append_query_params + + ### add connection pool + pool timeout args + params = {"connection_limit": 100, "pool_timeout": 60} + database_url = os.getenv("DATABASE_URL") + modified_url = append_query_params(database_url, params) + os.environ["DATABASE_URL"] = modified_url + # Assuming DBClient is a class that needs to be instantiated prisma_client = PrismaClient( database_url=os.environ["DATABASE_URL"], proxy_logging_obj=proxy_logging_obj @@ -1633,3 +1641,99 @@ async def test_key_with_no_permissions(prisma_client): except Exception as e: print("Got Exception", e) print(e.message) + + +async def track_cost_callback_helper_fn(generated_key: str, user_id: str): + from litellm import ModelResponse, Choices, Message, Usage + from litellm.proxy.proxy_server import ( + _PROXY_track_cost_callback as track_cost_callback, + ) + + import uuid + + request_id = f"chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac{uuid.uuid4()}" + 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={ + "call_type": "acompletion", + "model": "sagemaker-chatgpt-v-2", + "stream": True, + "complete_streaming_response": resp, + "litellm_params": { + "metadata": { + "user_api_key": hash_token(generated_key), + "user_api_key_user_id": user_id, + } + }, + "response_cost": 0.00005, + }, + completion_response=resp, + start_time=datetime.now(), + end_time=datetime.now(), + ) + + +# @pytest.mark.skip(reason="High traffic load test for spend tracking") +@pytest.mark.asyncio +async def test_proxy_load_test_db(prisma_client): + """ + Run 1500 req./s against track_cost_callback function + """ + setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) + setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") + from litellm._logging import verbose_proxy_logger + import logging, time + + litellm.set_verbose = True + verbose_proxy_logger.setLevel(logging.DEBUG) + try: + start_time = time.time() + await litellm.proxy.proxy_server.prisma_client.connect() + request = GenerateKeyRequest(max_budget=0.00001) + 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") + + # 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 + n = 5000 + tasks = [ + track_cost_callback_helper_fn(generated_key=generated_key, user_id=user_id) + for _ in range(n) + ] + completions = await asyncio.gather(*tasks) + await asyncio.sleep(120) + try: + # call spend logs + spend_logs = await view_spend_logs(api_key=generated_key) + + print(f"len responses: {len(spend_logs)}") + assert len(spend_logs) == n + print(n, time.time() - start_time, len(spend_logs)) + except: + print(n, time.time() - start_time, 0) + raise Exception(f"it worked! key={key.key}") + except Exception as e: + pytest.fail(f"An exception occurred - {str(e)}") diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 33e9c4fa31f..1a6b47985c0 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -313,6 +313,11 @@ export const userSpendLogsCall = async ( endTime: String ) => { try { + console.log(`user role in spend logs call: ${userRole}`); + if (userRole == "Admin") { + return await adminSpendLogsCall(accessToken); + } + let url = proxyBaseUrl ? `${proxyBaseUrl}/spend/logs` : `/spend/logs`; if (userRole == "App Owner") { url = `${url}/?user_id=${userID}&start_date=${startTime}&end_date=${endTime}`; @@ -343,6 +348,36 @@ export const userSpendLogsCall = async ( } }; +export const adminSpendLogsCall = async (accessToken: String) => { + try { + let url = proxyBaseUrl + ? `${proxyBaseUrl}/global/spend/logs` + : `/global/spend/logs`; + + message.info("Making spend logs request"); + const response = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + if (!response.ok) { + const errorData = await response.text(); + message.error(errorData); + throw new Error("Network response was not ok"); + } + + const data = await response.json(); + console.log(data); + message.success("Spend Logs received"); + return data; + } catch (error) { + console.error("Failed to create key:", error); + throw error; + } +}; + export const keyInfoCall = async (accessToken: String, keys: String[]) => { try { let url = proxyBaseUrl ? `${proxyBaseUrl}/v2/key/info` : `/v2/key/info`; diff --git a/ui/litellm-dashboard/src/components/usage.tsx b/ui/litellm-dashboard/src/components/usage.tsx index 53493eabce1..1b60df3d475 100644 --- a/ui/litellm-dashboard/src/components/usage.tsx +++ b/ui/litellm-dashboard/src/components/usage.tsx @@ -2,7 +2,11 @@ import { BarChart, Card, Title } from "@tremor/react"; import React, { useState, useEffect } from "react"; import { Grid, Col, Text, LineChart } from "@tremor/react"; -import { userSpendLogsCall, keyInfoCall } from "./networking"; +import { + userSpendLogsCall, + keyInfoCall, + adminSpendLogsCall, +} from "./networking"; import { start } from "repl"; interface UsagePageProps { @@ -175,27 +179,26 @@ const UsagePage: React.FC = ({ console.log("result from spend logs call", response); if ("daily_spend" in response) { // this is from clickhouse analytics - // + // let daily_spend = response["daily_spend"]; console.log("daily spend", daily_spend); setKeySpendData(daily_spend); let topApiKeys = response.top_api_keys; setTopKeys(topApiKeys); - } - else { - const topKeysResponse = await keyInfoCall( - accessToken, - getTopKeys(response) - ); - const filtered_keys = topKeysResponse["info"].map((k: any) => ({ - key: (k["key_name"] || k["key_alias"] || k["token"]).substring( - 0, - 7 - ), - spend: k["spend"], - })); - setTopKeys(filtered_keys); - setTopUsers(getTopUsers(response)); + } else { + // const topKeysResponse = await keyInfoCall( + // accessToken, + // getTopKeys(response) + // ); + // const filtered_keys = topKeysResponse["info"].map((k: any) => ({ + // key: (k["key_name"] || k["key_alias"] || k["token"]).substring( + // 0, + // 7 + // ), + // spend: k["spend"], + // })); + // setTopKeys(filtered_keys); + // setTopUsers(getTopUsers(response)); setKeySpendData(response); } }); @@ -222,7 +225,7 @@ const UsagePage: React.FC = ({ valueFormatter={valueFormatter} yAxisWidth={100} tickGap={5} - customTooltip={customTooltip} + // customTooltip={customTooltip} /> From 1f28867d2041af49aab3f6a20d4a53f8472f309b Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 13:08:38 -0800 Subject: [PATCH 02/12] fix(proxy/utils.py): add script for adding MonthlyGlobalSpend view to the db --- litellm/proxy/_experimental/out/404.html | 2 +- .../chunks/app/page-cc9d300e3b13fc1b.js | 1 + .../chunks/app/page-d4fe4a48cbd3572c.js | 1 - .../_buildManifest.js | 0 .../_ssgManifest.js | 0 litellm/proxy/_experimental/out/index.html | 2 +- litellm/proxy/_experimental/out/index.txt | 4 +-- litellm/proxy/proxy_server.py | 3 +- litellm/proxy/utils.py | 34 ++++++++++++++++--- ui/litellm-dashboard/out/404.html | 2 +- ui/litellm-dashboard/out/index.html | 2 +- ui/litellm-dashboard/out/index.txt | 4 +-- 12 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-cc9d300e3b13fc1b.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-d4fe4a48cbd3572c.js rename litellm/proxy/_experimental/out/_next/static/{eSwVwl_InIrhYtCAqDMKF => h6IXdBMiZG7ES547qg1M-}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{eSwVwl_InIrhYtCAqDMKF => h6IXdBMiZG7ES547qg1M-}/_ssgManifest.js (100%) diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html index 554dcf93ae5..687a52941c0 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/chunks/app/page-cc9d300e3b13fc1b.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-cc9d300e3b13fc1b.js new file mode 100644 index 00000000000..7f6ada01e1a --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-cc9d300e3b13fc1b.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{79615:function(e,t,s){Promise.resolve().then(s.bind(s,24143))},24143:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return eS}});var l=s(3827),n=s(64090),r=s(47907),a=s(8792),o=s(2179),i=e=>{let{userID:t,userRole:s,userEmail:n,showSSOBanner:r}=e;return console.log("User ID:",t),console.log("userEmail:",n),(0,l.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,l.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,l.jsx)("div",{className:"flex flex-col items-center",children:(0,l.jsx)(a.default,{href:"/",children:(0,l.jsx)("button",{className:"text-gray-800 text-2xl py-1 rounded text-center",children:(0,l.jsx)("img",{src:"/get_image",width:200,height:200,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,l.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[r?(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#setup-ssoauth-for-ui",target:"_blank",className:"mr-2",children:(0,l.jsx)(o.Z,{variant:"primary",size:"lg",children:"Enable SSO"})}):null,(0,l.jsxs)(o.Z,{variant:"secondary",size:"lg",children:[n,(0,l.jsxs)("p",{children:["Role: ",s]}),(0,l.jsxs)("p",{children:["ID: ",t]})]})]})]})},c=s(80588);let d=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let n=await l.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/user/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let n=await l.json();return console.log("API Response:",n),n}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 s=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),c.ZP.success("API Key Deleted"),l}catch(e){throw console.error("Failed to create key:",e),e}},u=async function(e,t,s){let l=arguments.length>3&&void 0!==arguments[3]&&arguments[3];try{let n="/user/info";"App Owner"==s&&t&&(n="".concat(n,"/?user_id=").concat(t)),console.log("in userInfoCall viewAll=",l),l&&(n="".concat(n,"/?view_all=true")),c.ZP.info("Requesting user data");let r=await fetch(n,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw c.ZP.error(e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),c.ZP.info("Received user data"),a}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t,s)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/v2/model/info",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return c.ZP.info("Received model data"),s}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,t,s)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/models",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return c.ZP.info("Received model data"),s}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let s="/spend/logs";console.log("in keySpendLogsCall:",s);let l=await fetch("".concat(s,"/?api_key=").concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let n=await l.json();return console.log(n),n}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t,s,l,n,r)=>{try{if(console.log("user role in spend logs call: ".concat(s)),"Admin"==s)return await Z(e);let t="/spend/logs";t="App Owner"==s?"".concat(t,"/?user_id=").concat(l,"&start_date=").concat(n,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(n,"&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}},Z=async e=>{try{c.ZP.info("Making spend logs request");let t=await fetch("/global/spend/logs",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return console.log(s),c.ZP.success("Spend Logs received"),s}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,t)=>{try{let s="/spend/users";console.log("in spendUsersCall:",s);let l=await fetch("".concat(s,"/?user_id=").concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let n=await l.json();return console.log(n),n}catch(e){throw console.error("Failed to get spend for user",e),e}},f=async(e,t,s,l)=>{try{let n=await fetch("/user/request_model",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({models:[t],user_id:s,justification:l})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let r=await n.json();return console.log(r),c.ZP.success(""),r}catch(e){throw console.error("Failed to create key:",e),e}},w=async e=>{try{let t="/user/get_requests";console.log("in userGetRequesedtModelsCall:",t);let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),c.ZP.success(""),l}catch(e){throw console.error("Failed to get requested models:",e),e}},k=async(e,t)=>{try{console.log("Form Values in teamCreateCall:",t);let s=await fetch("/team/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let l=await s.json();return console.log("API Response:",l),l}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,t,s)=>{try{console.log("Form Values in teamMemberAddCall:",s);let l=await fetch("/team/member_add",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let n=await l.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}};var b=s(10384),v=s(46453),S=s(71801),N=s(17189),I=s(12143),C=s(77171),A=s(42539),T=s(88707),P=s(1861);let{Option:E}=N.default;var D=e=>{let{userID:t,teamID:s,userRole:r,accessToken:a,data:i,userModels:h,setData:m}=e,[u]=I.Z.useForm(),[x,p]=(0,n.useState)(!1),[j,g]=(0,n.useState)(null),Z=()=>{p(!1),u.resetFields()},y=()=>{p(!1),g(null),u.resetFields()},f=async e=>{try{c.ZP.info("Making API Call"),p(!0);let s=await d(a,t,e);m(e=>e?[...e,s]:[s]),g(s.key),c.ZP.success("API Key Created"),u.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>p(!0),children:"+ Create New Key"}),(0,l.jsx)(C.Z,{title:"Create Key",visible:x,width:800,footer:null,onOk:Z,onCancel:y,children:(0,l.jsxs)(I.Z,{form:u,onFinish:f,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===r||"Admin"===r?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(I.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(A.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Team ID",name:"team_id",children:(0,l.jsx)(A.Z,{placeholder:"ai_team",defaultValue:s||""})}),(0,l.jsx)(I.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(N.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:h.map(e=>(0,l.jsx)(E,{value:e,children:e},e))})}),(0,l.jsx)(I.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(T.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(I.Z.Item,{label:"Reset Budget",name:"budget_duration",children:(0,l.jsxs)(N.default,{defaultValue:null,placeholder:"n/a",children:[(0,l.jsx)(N.default.Option,{value:"24h",children:"daily"}),(0,l.jsx)(N.default.Option,{value:"30d",children:"monthly"})]})}),(0,l.jsx)(I.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(T.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(T.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(A.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(A.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(I.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(A.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,l.jsx)(A.Z,{placeholder:"ai_team"})}),(0,l.jsx)(I.Z.Item,{label:"Description",name:"description",children:(0,l.jsx)(A.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(P.ZP,{htmlType:"submit",children:"Create Key"})})]})}),j&&(0,l.jsx)(C.Z,{title:"Save your key",visible:x,onOk:Z,onCancel:y,footer:null,children:(0,l.jsxs)(v.Z,{numItems:1,className:"gap-2 w-full",children:[(0,l.jsx)(b.Z,{numColSpan:1,children:(0,l.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,l.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,l.jsx)(b.Z,{numColSpan:1,children:null!=j?(0,l.jsxs)(S.Z,{children:["API Key: ",j]}):(0,l.jsx)(S.Z,{children:"Key being created, this might take 30s"})})]})})]})},F=s(33393),M=s(13810),R=s(61244),O=s(10827),U=s(3851),L=s(2044),z=s(64167),q=s(74480),B=s(7178),K=s(9853),J=s(67989),V=s(56863),G=s(42440),$=e=>{let{token:t,accessToken:s,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,n.useState)(!1),[h,m]=(0,n.useState)(null),[u,x]=(0,n.useState)(null),p=async()=>{try{if(null==s||null==t)return;console.log("accessToken: ".concat(s,"; token: ").concat(t));let e=await j(s,t);console.log("Response:",e);let l=Object.values(e).reduce((e,t)=>{let s=new Date(t.startTime),l=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(s);return e[l]=(e[l]||0)+t.spend,e},{}),n=Object.entries(l);n.sort((e,t)=>{let[s]=e,[l]=t,n=new Date(s),r=new Date(l);return n.getTime()-r.getTime()});let r=Object.fromEntries(n);console.log(r);let a=Object.values(e).reduce((e,t)=>{let s=t.user;return e[s]=(e[s]||0)+t.spend,e},{});console.log(l),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,s]=e;return{name:t,value:s}});m(o),x(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),p()},children:"View Spend Report"}),(0,l.jsxs)(C.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,l.jsxs)(G.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,l.jsxs)(V.Z,{children:["Monthly Spend $",r]}),(0,l.jsx)(M.Z,{className:"mt-6 mb-6",children:h&&(0,l.jsx)(K.Z,{className:"mt-6",data:h,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,l.jsx)(G.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,l.jsx)(M.Z,{className:"mb-6",children:u&&(0,l.jsx)(J.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},Y=e=>{let{userID:t,accessToken:s,data:r,setData:a}=e,[i,c]=(0,n.useState)(!1),[d,h]=(0,n.useState)(!1),[u,x]=(0,n.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(s,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,l.jsxs)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,l.jsxs)(O.Z,{className:"mt-5",children:[(0,l.jsx)(z.Z,{children:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(q.Z,{children:"Key Alias"}),(0,l.jsx)(q.Z,{children:"Secret Key"}),(0,l.jsx)(q.Z,{children:"Spend (USD)"}),(0,l.jsx)(q.Z,{children:"Key Budget (USD)"}),(0,l.jsx)(q.Z,{children:"Team ID"}),(0,l.jsx)(q.Z,{children:"Metadata"}),(0,l.jsx)(q.Z,{children:"Models"}),(0,l.jsx)(q.Z,{children:"TPM / RPM Limits"}),(0,l.jsx)(q.Z,{children:"Expires"})]})}),(0,l.jsx)(U.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:null!=e.key_alias?(0,l.jsx)(S.Z,{children:e.key_alias}):(0,l.jsx)(S.Z,{children:"Not Set"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:e.key_name})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:e.spend})}),(0,l.jsx)(L.Z,{children:null!=e.max_budget?(0,l.jsx)(S.Z,{children:e.max_budget}):(0,l.jsx)(S.Z,{children:"Unlimited Budget"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:e.team_id})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:JSON.stringify(e.metadata)})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:JSON.stringify(e.models)})}),(0,l.jsx)(L.Z,{children:(0,l.jsxs)(S.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,l.jsx)(L.Z,{children:null!=e.expires?(0,l.jsx)(S.Z,{children:e.expires}):(0,l.jsx)(S.Z,{children:"Never expires"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(R.Z,{onClick:()=>p(e.token),icon:F.Z,size:"sm"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)($,{token:e.token,accessToken:s,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,l.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,l.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,l.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,l.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,l.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,l.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,l.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,l.jsx)("div",{className:"sm:flex sm:items-start",children:(0,l.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,l.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,l.jsx)("div",{className:"mt-2",children:(0,l.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,l.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,l.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,l.jsx)(o.Z,{onClick:()=>{h(!1),x(null)},children:"Cancel"})]})]})]})})]})},W=e=>{let{userID:t,userSpendData:s,userRole:r,accessToken:a}=e;console.log("User SpendData:",s);let[o,i]=(0,n.useState)(null==s?void 0:s.spend),[c,d]=(0,n.useState)((null==s?void 0:s.max_budget)||null);return(0,n.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await y(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,l.jsx)(l.Fragment,{children:(0,l.jsxs)(M.Z,{className:"mx-auto mb-4",children:[(0,l.jsxs)(V.Z,{children:["$",o]}),(0,l.jsxs)(G.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},H=s(36083),X=s(68967),Q=s(27166),ee=e=>{let{teams:t,setSelectedTeam:s}=e,{Title:r,Paragraph:a}=H.default,[o,i]=(0,n.useState)("");return(0,l.jsxs)("div",{className:"mt-10",children:[(0,l.jsx)(r,{level:4,children:"Default Team"}),(0,l.jsx)(a,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),t&&t.length>0?(0,l.jsx)(X.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(Q.Z,{value:String(t),onClick:()=>s(e),children:e.team_alias},t))}):(0,l.jsxs)(a,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]})},et=s(37963);console.log("isLocal:",!1);var es=e=>{let{userID:t,userRole:s,teams:a,setUserRole:o,userEmail:i,setUserEmail:c,setTeams:d}=e,[h,m]=(0,n.useState)(null),[x,j]=(0,n.useState)(null),g=(0,r.useSearchParams)();g.get("viewSpend"),(0,r.useRouter)();let Z=g.get("token"),[y,f]=(0,n.useState)(null),[w,k]=(0,n.useState)([]),[_,S]=(0,n.useState)(a?a[0]:null);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,n.useEffect)(()=>{if(Z){let e=(0,et.o)(Z);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),f(e.key),e.user_role){let t=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),o(t)}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&y&&s&&!h){let e=sessionStorage.getItem("userModels"+t);e?k(JSON.parse(e)):(async()=>{try{let e=await u(y,t,s);console.log("received teams in user dashboard: ".concat(Object.keys(e),"; team values: ").concat(Object.entries(e.teams))),j(e.user_info),m(e.keys),d(e.teams),S(e.teams?e.teams[0]:null),sessionStorage.setItem("userData"+t,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let l=(await p(y,t,s)).data.map(e=>e.id);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,Z,y,h,s]),null==t||null==Z){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==y?null:(null==s&&o("App Owner"),(0,l.jsx)("div",{children:(0,l.jsx)(v.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(W,{userID:t,userSpendData:x,userRole:s,accessToken:y}),(0,l.jsx)(Y,{userID:t,accessToken:y,data:h,setData:m}),(0,l.jsx)(D,{userID:t,teamID:_?_.team_id:null,userRole:s,userModels:w,accessToken:y,data:h,setData:m}),(0,l.jsx)(ee,{teams:a,setSelectedTeam:S})]})})}))},el=s(5);let{Option:en}=N.default;var er=e=>{let{userModels:t,accessToken:s,userID:r}=e,[a]=I.Z.useForm(),[i,d]=(0,n.useState)(!1),h=async e=>{try{c.ZP.info("Requesting access");let{selectedModel:t,accessReason:l}=e;await f(s,t,r,l),d(!0)}catch(e){console.error("Error requesting access:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{size:"xs",onClick:()=>d(!0),children:"Request Access"}),(0,l.jsx)(C.Z,{title:"Request Access",visible:i,width:800,footer:null,onOk:()=>{d(!1),a.resetFields()},onCancel:()=>{d(!1),a.resetFields()},children:(0,l.jsxs)(I.Z,{form:a,onFinish:h,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(I.Z.Item,{label:"Select Model",name:"selectedModel",children:(0,l.jsx)(N.default,{placeholder:"Select model",style:{width:"100%"},children:t.map(e=>(0,l.jsx)(en,{value:e,children:e},e))})}),(0,l.jsx)(I.Z.Item,{label:"Reason for Access",name:"accessReason",children:(0,l.jsx)(A.Z.TextArea,{rows:4,placeholder:"Enter reason for access"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(o.Z,{children:"Request Access"})})]})})]})},ea=e=>{let{accessToken:t,token:s,userRole:r,userID:a}=e,[i,c]=(0,n.useState)({data:[]}),[d,h]=(0,n.useState)([]);if((0,n.useEffect)(()=>{if(!t||!s||!r||!a)return;let e=async()=>{try{let e=await x(t,a,r);if(console.log("Model data response:",e.data),c(e),"Admin"===r&&t){let e=await w(t);console.log("Pending Requests:",d),h(e.requests||[])}}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&r&&a&&e()},[t,s,r,a]),!i||!t||!s||!r||!a)return(0,l.jsx)("div",{children:"Loading..."});let m=[];for(let e=0;e(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.model_name})}),(0,l.jsx)(L.Z,{children:e.provider}),(0,l.jsx)(L.Z,{children:e.user_access?(0,l.jsx)(el.Z,{color:"green",children:"Yes"}):(0,l.jsx)(er,{userModels:m,accessToken:t,userID:a})}),(0,l.jsx)(L.Z,{children:e.input_cost}),(0,l.jsx)(L.Z,{children:e.output_cost}),(0,l.jsx)(L.Z,{children:e.max_tokens})]},e.model_name))})]})}),"Admin"===r&&d&&d.length>0?(0,l.jsx)(M.Z,{children:(0,l.jsxs)(O.Z,{children:[(0,l.jsxs)(z.Z,{children:[(0,l.jsx)(G.Z,{children:"Pending Requests"}),(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User ID"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"Requested Models"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"Justification"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"Justification"})})]})]}),(0,l.jsx)(U.Z,{children:d.map(e=>(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)("p",{children:e.user_id})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)("p",{children:e.models[0]})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)("p",{children:e.justification})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)("p",{children:e.user_id})}),(0,l.jsx)(o.Z,{children:"Approve"}),(0,l.jsx)(o.Z,{variant:"secondary",className:"ml-2",children:"Deny"})]},e.request_id))})]})}):null]})})};let{Option:eo}=N.default;var ei=e=>{let{userID:t,accessToken:s}=e,[r]=I.Z.useForm(),[a,i]=(0,n.useState)(!1),[d,m]=(0,n.useState)(null),[u,x]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{let e=await p(s,t,"any"),l=[];for(let t=0;t{i(!1),r.resetFields()},g=()=>{i(!1),m(null),r.resetFields()},Z=async e=>{try{c.ZP.info("Making API Call"),i(!0),console.log("formValues in create user:",e);let l=await h(s,t,e);console.log("user create Response:",l),m(l.key),c.ZP.success("API user Created"),r.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the user:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>i(!0),children:"+ Create New User"}),(0,l.jsx)(C.Z,{title:"Create User",visible:a,width:800,footer:null,onOk:j,onCancel:g,children:(0,l.jsxs)(I.Z,{form:r,onFinish:Z,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(I.Z.Item,{label:"User ID",name:"user_id",children:(0,l.jsx)(A.Z,{placeholder:"Enter User ID"})}),(0,l.jsx)(I.Z.Item,{label:"Team ID",name:"team_id",children:(0,l.jsx)(A.Z,{placeholder:"ai_team"})}),(0,l.jsx)(I.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(N.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:u.map(e=>(0,l.jsx)(eo,{value:e,children:e},e))})}),(0,l.jsx)(I.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(T.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(I.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(T.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(T.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(A.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(A.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(P.ZP,{htmlType:"submit",children:"Create User"})})]})}),d&&(0,l.jsxs)(C.Z,{title:"Save Your User",visible:a,onOk:j,onCancel:g,footer:null,children:[(0,l.jsxs)("p",{children:["Please save this secret user somewhere safe and accessible. For security reasons, ",(0,l.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret user, you will need to generate a new one."]}),(0,l.jsx)("p",{children:null!=d?"API user: ".concat(d):"User being created, this might take 30s"})]})]})},ec=e=>{let{accessToken:t,token:s,userRole:r,userID:a}=e,[o,i]=(0,n.useState)(null),[c,d]=(0,n.useState)([]);return((0,n.useEffect)(()=>{if(!t||!s||!r||!a)return;let e=async()=>{try{let e=await u(t,null,r,!0);console.log("user data response:",e),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&r&&a&&e()},[t,s,r,a]),o&&t&&s&&r&&a)?(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(v.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(ei,{userID:a,accessToken:t}),(0,l.jsx)(M.Z,{children:(0,l.jsxs)(O.Z,{className:"mt-5",children:[(0,l.jsx)(z.Z,{children:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User ID "})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User Role"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User Models"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User Spend ($ USD)"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User Max Budget ($ USD)"})})]})}),(0,l.jsx)(U.Z,{children:o.map(e=>(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.user_id})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.user_role?e.user_role:"app_user"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.models&&e.models.length>0?e.models:"All Models"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.spend?e.spend:0})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.max_budget?e.max_budget:"Unlimited"})})]},e.user_id))})]})})]})}):(0,l.jsx)("div",{children:"Loading..."})},ed=s(8510),eh=e=>{let{teams:t,searchParams:s,accessToken:r,setTeams:a}=e,[i]=I.Z.useForm(),[d]=I.Z.useForm(),{Title:h,Paragraph:m}=H.default,[u,x]=(0,n.useState)(""),[p,j]=(0,n.useState)(t?t[0]:null),[g,Z]=(0,n.useState)(!1),[y,f]=(0,n.useState)(!1),w=async e=>{try{if(null!=r){c.ZP.info("Making API Call");let s=await k(r,e);null!==t?a([...t,s]):a([s]),console.log("response for team create call: ".concat(s)),Z(!1)}}catch(e){console.error("Error creating the key:",e)}},E=async e=>{try{if(null!=r&&null!=t){c.ZP.info("Making API Call");let s={role:"user",user_email:e.user_email,user_id:e.user_id},l=await _(r,p.team_id,s);console.log("response for team create call: ".concat(l.data));let n=t.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(l.data.team_id)),e.team_id===l.data.team_id));if(console.log("foundIndex: ".concat(n)),-1!==n){let e=[...t];e[n]=l.data,a(e),j(l.data)}f(!1)}}catch(e){console.error("Error creating the key:",e)}};return console.log("received teams ".concat(t)),(0,l.jsx)("div",{className:"w-full",children:(0,l.jsxs)(v.Z,{numItems:1,className:"gap-2 p-2 h-[75vh] w-full",children:[(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(h,{level:4,children:"All Teams"}),(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)(O.Z,{children:[(0,l.jsx)(z.Z,{children:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(q.Z,{children:"Team Name"}),(0,l.jsx)(q.Z,{children:"Spend (USD)"}),(0,l.jsx)(q.Z,{children:"Budget (USD)"}),(0,l.jsx)(q.Z,{children:"TPM / RPM Limits"})]})}),(0,l.jsx)(U.Z,{children:t&&t.length>0?t.map(e=>(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:e.team_alias}),(0,l.jsx)(L.Z,{children:e.spend}),(0,l.jsx)(L.Z,{children:e.max_budget?e.max_budget:"No limit"}),(0,l.jsx)(L.Z,{children:(0,l.jsxs)(S.Z,{children:["TPM Limit:"," ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(R.Z,{icon:ed.Z,size:"sm"})})]},e.team_id)):null})]})})]}),(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>Z(!0),children:"+ Create New Team"}),(0,l.jsx)(C.Z,{title:"Create Team",visible:g,width:800,footer:null,onOk:()=>{Z(!1),i.resetFields()},onCancel:()=>{Z(!1),i.resetFields()},children:(0,l.jsxs)(I.Z,{form:i,onFinish:w,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(I.Z.Item,{label:"Team Name",name:"team_alias",children:(0,l.jsx)(A.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(N.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"}})}),(0,l.jsx)(I.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(T.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(I.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(T.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(T.Z,{step:1,width:400})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(P.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(h,{level:4,children:"Team Members"}),(0,l.jsx)(m,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),t&&t.length>0?(0,l.jsx)(X.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(Q.Z,{value:String(t),onClick:()=>{j(e)},children:e.team_alias},t))}):(0,l.jsxs)(m,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,l.jsx)(b.Z,{numColSpan:1,children:(0,l.jsx)(M.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)(O.Z,{children:[(0,l.jsx)(z.Z,{children:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(q.Z,{children:"Member Name"}),(0,l.jsx)(q.Z,{children:"Role"}),(0,l.jsx)(q.Z,{children:"Action"})]})}),(0,l.jsx)(U.Z,{children:p?p.members_with_roles.map((e,t)=>(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,l.jsx)(L.Z,{children:e.role}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(R.Z,{icon:ed.Z,size:"sm"})})]},t)):null})]})})}),(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto mb-5",onClick:()=>f(!0),children:"+ Add member"}),(0,l.jsx)(C.Z,{title:"Add member",visible:y,width:800,footer:null,onOk:()=>{f(!1),d.resetFields()},onCancel:()=>{f(!1),d.resetFields()},children:(0,l.jsxs)(I.Z,{form:i,onFinish:E,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(I.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,l.jsx)(A.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,l.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,l.jsx)(I.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,l.jsx)(A.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(P.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},em=s(92836),eu=s(26734),ex=s(41608),ep=s(32126),ej=s(23682),eg=s(12968),eZ=s(67951);async function ey(e,t,s,l){console.log("isLocal:",!1);let n=window.location.origin,r=new eg.ZP.OpenAI({apiKey:l,baseURL:n,dangerouslyAllowBrowser:!0});for await(let l of(await r.chat.completions.create({model:s,stream:!0,messages:[{role:"user",content:e}]})))console.log(l),l.choices[0].delta.content&&t(l.choices[0].delta.content)}var ef=e=>{let{accessToken:t,token:s,userRole:r,userID:a}=e,[o,i]=(0,n.useState)(""),[c,d]=(0,n.useState)([]),[h,m]=(0,n.useState)(void 0),[u,x]=(0,n.useState)(null);(0,n.useEffect)(()=>{t&&s&&r&&a&&(async()=>{let e=await p(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(x(e.data),m(e.data[0].id))})()},[t,a,r]);let j=(e,t)=>{d(s=>{let l=s[s.length-1];return l&&l.role===e?[...s.slice(0,s.length-1),{role:e,content:l.content+t}]:[...s,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&s&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{h&&await ey(o,e=>j("assistant",e),h,t)}catch(e){console.error("Error fetching model response",e),j("assistant","Error fetching model response")}i("")}};return(0,l.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,l.jsx)(v.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,l.jsx)(M.Z,{children:(0,l.jsxs)(eu.Z,{children:[(0,l.jsxs)(ex.Z,{className:"mt-4",children:[(0,l.jsx)(em.Z,{children:"Chat"}),(0,l.jsx)(em.Z,{children:"API Reference"})]}),(0,l.jsxs)(ej.Z,{children:[(0,l.jsxs)(ep.Z,{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{children:"Select Model:"}),(0,l.jsx)("select",{value:h||"",onChange:e=>m(e.target.value),children:null==u?void 0:u.map(e=>(0,l.jsx)("option",{value:e.id,children:e.id},e.id))})]}),(0,l.jsxs)(O.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,l.jsx)(z.Z,{children:(0,l.jsx)(B.Z,{children:(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"Chat"})})})}),(0,l.jsx)(U.Z,{children:c.map((e,t)=>(0,l.jsx)(B.Z,{children:(0,l.jsx)(L.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,l.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,l.jsxs)("div",{className:"flex",children:[(0,l.jsx)("input",{type:"text",value:o,onChange:e=>i(e.target.value),className:"flex-1 p-2 border rounded-md mr-2",placeholder:"Type your message..."}),(0,l.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,l.jsx)(ep.Z,{children:(0,l.jsxs)(eu.Z,{children:[(0,l.jsxs)(ex.Z,{children:[(0,l.jsx)(em.Z,{children:"OpenAI Python SDK"}),(0,l.jsx)(em.Z,{children:"LlamaIndex"}),(0,l.jsx)(em.Z,{children:"Langchain Py"})]}),(0,l.jsxs)(ej.Z,{children:[(0,l.jsx)(ep.Z,{children:(0,l.jsx)(eZ.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="http://0.0.0.0:4000" # proxy base url\n)\n\nresponse = client.chat.completions.create(\n model="gpt-3.5-turbo", # model to use from Models Tab\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ],\n extra_body={\n "metadata": {\n "generation_name": "ishaan-generation-openai-client",\n "generation_id": "openai-client-gen-id22",\n "trace_id": "openai-client-trace-id22",\n "trace_user_id": "openai-client-user-id2"\n }\n }\n)\n\nprint(response)\n '})}),(0,l.jsx)(ep.Z,{children:(0,l.jsx)(eZ.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="azure-gpt-3.5", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="http://0.0.0.0:4000",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n '})}),(0,l.jsx)(ep.Z,{children:(0,l.jsx)(eZ.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 '})})]})]})})]})]})})})})},ew=s(33509),ek=s(30569);let{Sider:e_}=ew.default;var eb=e=>{let{setPage:t,userRole:s,defaultSelectedKey:n}=e;return(0,l.jsx)(ew.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,l.jsx)(e_,{width:120,children:(0,l.jsxs)(ek.Z,{mode:"inline",defaultSelectedKeys:n||["1"],style:{height:"100%",borderRight:0},children:[(0,l.jsx)(ek.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,l.jsx)(ek.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,l.jsx)(ek.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,l.jsx)(ek.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4"),"Admin"==s?(0,l.jsx)(ek.Z.Item,{onClick:()=>t("users"),children:"Users"},"5"):null,"Admin"==s?(0,l.jsx)(ek.Z.Item,{onClick:()=>t("teams"),children:"Teams"},"6"):null]})})})},ev=e=>{let{accessToken:t,token:s,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,n.useState)([]),[d,h]=(0,n.useState)([]),[m,u]=(0,n.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),p=new Date(o.getFullYear(),o.getMonth()+1,0),j=y(x),Z=y(p);function y(e){let t=e.getFullYear(),s=e.getMonth()+1,l=e.getDate();return"".concat(t,"-").concat(s<10?"0"+s:s,"-").concat(l<10?"0"+l:l)}return console.log("Start date is ".concat(j)),console.log("End date is ".concat(Z)),(0,n.useEffect)(()=>{t&&s&&r&&a&&(async()=>{try{await g(t,s,r,a,j,Z).then(async e=>{if(console.log("result from spend logs call",e),"daily_spend"in e){let t=e.daily_spend;console.log("daily spend",t),c(t);let s=e.top_api_keys;h(s)}else c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,s,r,a,j,Z]),(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(v.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(b.Z,{numColSpan:2,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(G.Z,{children:"Monthly Spend"}),(0,l.jsx)(K.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,l.jsx)(b.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(G.Z,{children:"Top API Keys"}),(0,l.jsx)(K.Z,{className:"mt-4 h-40",data:d,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,l.jsx)(b.Z,{numColSpan:1,children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(G.Z,{children:"Top Users"}),(0,l.jsx)(K.Z,{className:"mt-4 h-40",data:m,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},eS=()=>{let[e,t]=(0,n.useState)(""),[s,a]=(0,n.useState)(null),[o,c]=(0,n.useState)(null),[d,h]=(0,n.useState)(!0),m=(0,r.useSearchParams)(),u=m.get("userID"),x=m.get("token"),[p,j]=(0,n.useState)("api-keys"),[g,Z]=(0,n.useState)(null);return(0,n.useEffect)(()=>{if(x){let e=(0,et.o)(x);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(e.key),e.user_role){let s=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:",s),t(s)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?h("username_password"==e.login_method):console.log("User Email is not set ".concat(e))}}},[x]),(0,l.jsx)(n.Suspense,{fallback:(0,l.jsx)("div",{children:"Loading..."}),children:(0,l.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,l.jsx)(i,{userID:u,userRole:e,userEmail:s,showSSOBanner:d}),(0,l.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,l.jsx)(eb,{setPage:j,userRole:e,defaultSelectedKey:null}),"api-keys"==p?(0,l.jsx)(es,{userID:u,userRole:e,teams:o,setUserRole:t,userEmail:s,setUserEmail:a,setTeams:c}):"models"==p?(0,l.jsx)(ea,{userID:u,userRole:e,token:x,accessToken:g}):"llm-playground"==p?(0,l.jsx)(ef,{userID:u,userRole:e,token:x,accessToken:g}):"users"==p?(0,l.jsx)(ec,{userID:u,userRole:e,token:x,accessToken:g}):"teams"==p?(0,l.jsx)(eh,{teams:o,setTeams:c,searchParams:m,accessToken:g}):(0,l.jsx)(ev,{userID:u,userRole:e,token:x,accessToken:g})]})]})})}}},function(e){e.O(0,[303,971,69,744],function(){return e(e.s=79615)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-d4fe4a48cbd3572c.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-d4fe4a48cbd3572c.js deleted file mode 100644 index d1b147c028b..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-d4fe4a48cbd3572c.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{79615:function(e,t,s){Promise.resolve().then(s.bind(s,24143))},24143:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return eN}});var l=s(3827),n=s(64090),r=s(47907),a=s(8792),o=s(2179),i=e=>{let{userID:t,userRole:s,userEmail:n,showSSOBanner:r}=e;return console.log("User ID:",t),console.log("userEmail:",n),(0,l.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,l.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,l.jsx)("div",{className:"flex flex-col items-center",children:(0,l.jsx)(a.default,{href:"/",children:(0,l.jsx)("button",{className:"text-gray-800 text-2xl py-1 rounded text-center",children:(0,l.jsx)("img",{src:"/get_image",width:200,height:200,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,l.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[r?(0,l.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#setup-ssoauth-for-ui",target:"_blank",className:"mr-2",children:(0,l.jsx)(o.Z,{variant:"primary",size:"lg",children:"Enable SSO"})}):null,(0,l.jsxs)(o.Z,{variant:"secondary",size:"lg",children:[n,(0,l.jsxs)("p",{children:["Role: ",s]}),(0,l.jsxs)("p",{children:["ID: ",t]})]})]})]})},c=s(80588);let d=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let n=await l.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},m=async(e,t,s)=>{try{if(console.log("Form Values in keyCreateCall:",s),s.description&&(s.metadata||(s.metadata={}),s.metadata.description=s.description,delete s.description,s.metadata=JSON.stringify(s.metadata)),s.metadata){console.log("formValues.metadata:",s.metadata);try{s.metadata=JSON.parse(s.metadata)}catch(e){throw c.ZP.error("Failed to parse metadata: "+e),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",s);let l=await fetch("/user/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let n=await l.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},h=async(e,t)=>{try{console.log("in keyDeleteCall:",t),c.ZP.info("Making key delete request");let s=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),c.ZP.success("API Key Deleted"),l}catch(e){throw console.error("Failed to create key:",e),e}},u=async function(e,t,s){let l=arguments.length>3&&void 0!==arguments[3]&&arguments[3];try{let n="/user/info";"App Owner"==s&&t&&(n="".concat(n,"/?user_id=").concat(t)),console.log("in userInfoCall viewAll=",l),l&&(n="".concat(n,"/?view_all=true")),c.ZP.info("Requesting user data");let r=await fetch(n,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw c.ZP.error(e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),c.ZP.info("Received user data"),a}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t,s)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/v2/model/info",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return c.ZP.info("Received model data"),s}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,t,s)=>{try{c.ZP.info("Requesting model data");let t=await fetch("/models",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw c.ZP.error(e),Error("Network response was not ok")}let s=await t.json();return c.ZP.info("Received model data"),s}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let s="/spend/logs";console.log("in keySpendLogsCall:",s);let l=await fetch("".concat(s,"/?api_key=").concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let n=await l.json();return console.log(n),n}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t,s,l,n,r)=>{try{let t="/spend/logs";t="App Owner"==s?"".concat(t,"/?user_id=").concat(l,"&start_date=").concat(n,"&end_date=").concat(r):"".concat(t,"/?start_date=").concat(n,"&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}},Z=async(e,t)=>{try{let s=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!s.ok){let e=await s.text();throw c.ZP.error(e),Error("Network response was not ok")}let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,t)=>{try{let s="/spend/users";console.log("in spendUsersCall:",s);let l=await fetch("".concat(s,"/?user_id=").concat(t),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw c.ZP.error(e),Error("Network response was not ok")}let n=await l.json();return console.log(n),n}catch(e){throw console.error("Failed to get spend for user",e),e}},f=async(e,t,s,l)=>{try{let n=await fetch("/user/request_model",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({models:[t],user_id:s,justification:l})});if(!n.ok){let e=await n.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let r=await n.json();return console.log(r),c.ZP.success(""),r}catch(e){throw console.error("Failed to create key:",e),e}},w=async e=>{try{let t="/user/get_requests";console.log("in userGetRequesedtModelsCall:",t);let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to delete key: "+e),Error("Network response was not ok")}let l=await s.json();return console.log(l),c.ZP.success(""),l}catch(e){throw console.error("Failed to get requested models:",e),e}},k=async(e,t)=>{try{console.log("Form Values in teamCreateCall:",t);let s=await fetch("/team/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!s.ok){let e=await s.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let l=await s.json();return console.log("API Response:",l),l}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,t,s)=>{try{console.log("Form Values in teamMemberAddCall:",s);let l=await fetch("/team/member_add",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:s})});if(!l.ok){let e=await l.text();throw c.ZP.error("Failed to create key: "+e),console.error("Error response from the server:",e),Error("Network response was not ok")}let n=await l.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}};var b=s(10384),v=s(46453),S=s(71801),N=s(17189),I=s(12143),C=s(77171),T=s(42539),A=s(88707),P=s(1861);let{Option:E}=N.default;var O=e=>{let{userID:t,teamID:s,userRole:r,accessToken:a,data:i,userModels:m,setData:h}=e,[u]=I.Z.useForm(),[x,p]=(0,n.useState)(!1),[j,g]=(0,n.useState)(null),Z=()=>{p(!1),u.resetFields()},y=()=>{p(!1),g(null),u.resetFields()},f=async e=>{try{c.ZP.info("Making API Call"),p(!0);let s=await d(a,t,e);h(e=>e?[...e,s]:[s]),g(s.key),c.ZP.success("API Key Created"),u.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the key:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>p(!0),children:"+ Create New Key"}),(0,l.jsx)(C.Z,{title:"Create Key",visible:x,width:800,footer:null,onOk:Z,onCancel:y,children:(0,l.jsxs)(I.Z,{form:u,onFinish:f,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:["App Owner"===r||"Admin"===r?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(I.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(T.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Team ID",name:"team_id",children:(0,l.jsx)(T.Z,{placeholder:"ai_team",defaultValue:s||""})}),(0,l.jsx)(I.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(N.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:m.map(e=>(0,l.jsx)(E,{value:e,children:e},e))})}),(0,l.jsx)(I.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(I.Z.Item,{label:"Reset Budget",name:"budget_duration",children:(0,l.jsxs)(N.default,{defaultValue:null,placeholder:"n/a",children:[(0,l.jsx)(N.default.Option,{value:"24h",children:"daily"}),(0,l.jsx)(N.default.Option,{value:"30d",children:"monthly"})]})}),(0,l.jsx)(I.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(A.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(A.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(T.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(T.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]}):(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(I.Z.Item,{label:"Key Name",name:"key_alias",children:(0,l.jsx)(T.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Team ID (Contact Group)",name:"team_id",children:(0,l.jsx)(T.Z,{placeholder:"ai_team"})}),(0,l.jsx)(I.Z.Item,{label:"Description",name:"description",children:(0,l.jsx)(T.Z.TextArea,{placeholder:"Enter description",rows:4})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(P.ZP,{htmlType:"submit",children:"Create Key"})})]})}),j&&(0,l.jsx)(C.Z,{title:"Save your key",visible:x,onOk:Z,onCancel:y,footer:null,children:(0,l.jsxs)(v.Z,{numItems:1,className:"gap-2 w-full",children:[(0,l.jsx)(b.Z,{numColSpan:1,children:(0,l.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,l.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,l.jsx)(b.Z,{numColSpan:1,children:null!=j?(0,l.jsxs)(S.Z,{children:["API Key: ",j]}):(0,l.jsx)(S.Z,{children:"Key being created, this might take 30s"})})]})})]})},D=s(33393),F=s(13810),R=s(61244),M=s(10827),U=s(3851),L=s(2044),z=s(64167),q=s(74480),B=s(7178),K=s(9853),J=s(67989),V=s(56863),G=s(42440),$=e=>{let{token:t,accessToken:s,keySpend:r,keyBudget:a,keyName:i}=e,[c,d]=(0,n.useState)(!1),[m,h]=(0,n.useState)(null),[u,x]=(0,n.useState)(null),p=async()=>{try{if(null==s||null==t)return;console.log("accessToken: ".concat(s,"; token: ").concat(t));let e=await j(s,t);console.log("Response:",e);let l=Object.values(e).reduce((e,t)=>{let s=new Date(t.startTime),l=new Intl.DateTimeFormat("en-US",{day:"2-digit",month:"short"}).format(s);return e[l]=(e[l]||0)+t.spend,e},{}),n=Object.entries(l);n.sort((e,t)=>{let[s]=e,[l]=t,n=new Date(s),r=new Date(l);return n.getTime()-r.getTime()});let r=Object.fromEntries(n);console.log(r);let a=Object.values(e).reduce((e,t)=>{let s=t.user;return e[s]=(e[s]||0)+t.spend,e},{});console.log(l),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,s]=e;return{name:t,value:s}});h(o),x(i),console.log("arrayBarChart:",o)}catch(e){console.error("There was an error fetching the data",e)}};return t?(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>{console.log("Show Modal triggered"),d(!0),p()},children:"View Spend Report"}),(0,l.jsxs)(C.Z,{visible:c,width:1e3,onOk:()=>{d(!1)},onCancel:()=>{d(!1)},footer:null,children:[(0,l.jsxs)(G.Z,{style:{textAlign:"left"},children:["Key Name: ",i]}),(0,l.jsxs)(V.Z,{children:["Monthly Spend $",r]}),(0,l.jsx)(F.Z,{className:"mt-6 mb-6",children:m&&(0,l.jsx)(K.Z,{className:"mt-6",data:m,colors:["green"],index:"day",categories:["spend"],yAxisWidth:48})}),(0,l.jsx)(G.Z,{className:"mt-6",children:"Top 5 Users Spend (USD)"}),(0,l.jsx)(F.Z,{className:"mb-6",children:u&&(0,l.jsx)(J.Z,{className:"mt-6",data:u,color:"teal"})})]})]}):null},Y=e=>{let{userID:t,accessToken:s,data:r,setData:a}=e,[i,c]=(0,n.useState)(!1),[d,m]=(0,n.useState)(!1),[u,x]=(0,n.useState)(null),p=async e=>{null!=r&&(x(e),localStorage.removeItem("userData"+t),m(!0))},j=async()=>{if(null!=u&&null!=r){try{await h(s,u);let e=r.filter(e=>e.token!==u);a(e)}catch(e){console.error("Error deleting the key:",e)}m(!1),x(null)}};if(null!=r)return console.log("RERENDER TRIGGERED"),(0,l.jsxs)(F.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4",children:[(0,l.jsxs)(M.Z,{className:"mt-5",children:[(0,l.jsx)(z.Z,{children:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(q.Z,{children:"Key Alias"}),(0,l.jsx)(q.Z,{children:"Secret Key"}),(0,l.jsx)(q.Z,{children:"Spend (USD)"}),(0,l.jsx)(q.Z,{children:"Key Budget (USD)"}),(0,l.jsx)(q.Z,{children:"Team ID"}),(0,l.jsx)(q.Z,{children:"Metadata"}),(0,l.jsx)(q.Z,{children:"Models"}),(0,l.jsx)(q.Z,{children:"TPM / RPM Limits"}),(0,l.jsx)(q.Z,{children:"Expires"})]})}),(0,l.jsx)(U.Z,{children:r.map(e=>(console.log(e),"litellm-dashboard"===e.team_id)?null:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:null!=e.key_alias?(0,l.jsx)(S.Z,{children:e.key_alias}):(0,l.jsx)(S.Z,{children:"Not Set"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:e.key_name})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:e.spend})}),(0,l.jsx)(L.Z,{children:null!=e.max_budget?(0,l.jsx)(S.Z,{children:e.max_budget}):(0,l.jsx)(S.Z,{children:"Unlimited Budget"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:e.team_id})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:JSON.stringify(e.metadata)})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(S.Z,{children:JSON.stringify(e.models)})}),(0,l.jsx)(L.Z,{children:(0,l.jsxs)(S.Z,{children:["TPM Limit: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,l.jsx)(L.Z,{children:null!=e.expires?(0,l.jsx)(S.Z,{children:e.expires}):(0,l.jsx)(S.Z,{children:"Never expires"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(R.Z,{onClick:()=>p(e.token),icon:D.Z,size:"sm"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)($,{token:e.token,accessToken:s,keySpend:e.spend,keyBudget:e.max_budget,keyName:e.key_name})})]},e.token))})]}),d&&(0,l.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,l.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,l.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,l.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,l.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,l.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,l.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,l.jsx)("div",{className:"sm:flex sm:items-start",children:(0,l.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,l.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,l.jsx)("div",{className:"mt-2",children:(0,l.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,l.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,l.jsx)(o.Z,{onClick:j,color:"red",className:"ml-2",children:"Delete"}),(0,l.jsx)(o.Z,{onClick:()=>{m(!1),x(null)},children:"Cancel"})]})]})]})})]})},W=e=>{let{userID:t,userSpendData:s,userRole:r,accessToken:a}=e;console.log("User SpendData:",s);let[o,i]=(0,n.useState)(null==s?void 0:s.spend),[c,d]=(0,n.useState)((null==s?void 0:s.max_budget)||null);return(0,n.useEffect)(()=>{(async()=>{if("Admin"===r)try{let e=await y(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,l.jsx)(l.Fragment,{children:(0,l.jsxs)(F.Z,{className:"mx-auto mb-4",children:[(0,l.jsxs)(V.Z,{children:["$",o]}),(0,l.jsxs)(G.Z,{children:["/ ",null!==c?"$".concat(c," limit"):"No limit"]})]})})},H=s(36083),X=s(68967),Q=s(27166),ee=e=>{let{teams:t,setSelectedTeam:s}=e,{Title:r,Paragraph:a}=H.default,[o,i]=(0,n.useState)("");return(0,l.jsxs)("div",{className:"mt-10",children:[(0,l.jsx)(r,{level:4,children:"Default Team"}),(0,l.jsx)(a,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),t&&t.length>0?(0,l.jsx)(X.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(Q.Z,{value:String(t),onClick:()=>s(e),children:e.team_alias},t))}):(0,l.jsxs)(a,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]})},et=s(37963);console.log("isLocal:",!1);var es=e=>{let{userID:t,userRole:s,teams:a,setUserRole:o,userEmail:i,setUserEmail:c,setTeams:d}=e,[m,h]=(0,n.useState)(null),[x,j]=(0,n.useState)(null),g=(0,r.useSearchParams)();g.get("viewSpend"),(0,r.useRouter)();let Z=g.get("token"),[y,f]=(0,n.useState)(null),[w,k]=(0,n.useState)([]),[_,S]=(0,n.useState)(a?a[0]:null);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,n.useEffect)(()=>{if(Z){let e=(0,et.o)(Z);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),f(e.key),e.user_role){let t=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":return"Admin";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",t),o(t)}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e))}}if(t&&y&&s&&!m){let e=sessionStorage.getItem("userModels"+t);e?k(JSON.parse(e)):(async()=>{try{let e=await u(y,t,s);console.log("received teams in user dashboard: ".concat(Object.keys(e),"; team values: ").concat(Object.entries(e.teams))),j(e.user_info),h(e.keys),d(e.teams),S(e.teams?e.teams[0]:null),sessionStorage.setItem("userData"+t,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+t,JSON.stringify(e.user_info));let l=(await p(y,t,s)).data.map(e=>e.id);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,Z,y,m,s]),null==t||null==Z){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}return null==y?null:(null==s&&o("App Owner"),(0,l.jsx)("div",{children:(0,l.jsx)(v.Z,{numItems:1,className:"gap-0 p-10 h-[75vh] w-full",children:(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(W,{userID:t,userSpendData:x,userRole:s,accessToken:y}),(0,l.jsx)(Y,{userID:t,accessToken:y,data:m,setData:h}),(0,l.jsx)(O,{userID:t,teamID:_?_.team_id:null,userRole:s,userModels:w,accessToken:y,data:m,setData:h}),(0,l.jsx)(ee,{teams:a,setSelectedTeam:S})]})})}))},el=s(5);let{Option:en}=N.default;var er=e=>{let{userModels:t,accessToken:s,userID:r}=e,[a]=I.Z.useForm(),[i,d]=(0,n.useState)(!1),m=async e=>{try{c.ZP.info("Requesting access");let{selectedModel:t,accessReason:l}=e;await f(s,t,r,l),d(!0)}catch(e){console.error("Error requesting access:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{size:"xs",onClick:()=>d(!0),children:"Request Access"}),(0,l.jsx)(C.Z,{title:"Request Access",visible:i,width:800,footer:null,onOk:()=>{d(!1),a.resetFields()},onCancel:()=>{d(!1),a.resetFields()},children:(0,l.jsxs)(I.Z,{form:a,onFinish:m,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(I.Z.Item,{label:"Select Model",name:"selectedModel",children:(0,l.jsx)(N.default,{placeholder:"Select model",style:{width:"100%"},children:t.map(e=>(0,l.jsx)(en,{value:e,children:e},e))})}),(0,l.jsx)(I.Z.Item,{label:"Reason for Access",name:"accessReason",children:(0,l.jsx)(T.Z.TextArea,{rows:4,placeholder:"Enter reason for access"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(o.Z,{children:"Request Access"})})]})})]})},ea=e=>{let{accessToken:t,token:s,userRole:r,userID:a}=e,[i,c]=(0,n.useState)({data:[]}),[d,m]=(0,n.useState)([]);if((0,n.useEffect)(()=>{if(!t||!s||!r||!a)return;let e=async()=>{try{let e=await x(t,a,r);if(console.log("Model data response:",e.data),c(e),"Admin"===r&&t){let e=await w(t);console.log("Pending Requests:",d),m(e.requests||[])}}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&r&&a&&e()},[t,s,r,a]),!i||!t||!s||!r||!a)return(0,l.jsx)("div",{children:"Loading..."});let h=[];for(let e=0;e(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.model_name})}),(0,l.jsx)(L.Z,{children:e.provider}),(0,l.jsx)(L.Z,{children:e.user_access?(0,l.jsx)(el.Z,{color:"green",children:"Yes"}):(0,l.jsx)(er,{userModels:h,accessToken:t,userID:a})}),(0,l.jsx)(L.Z,{children:e.input_cost}),(0,l.jsx)(L.Z,{children:e.output_cost}),(0,l.jsx)(L.Z,{children:e.max_tokens})]},e.model_name))})]})}),"Admin"===r&&d&&d.length>0?(0,l.jsx)(F.Z,{children:(0,l.jsxs)(M.Z,{children:[(0,l.jsxs)(z.Z,{children:[(0,l.jsx)(G.Z,{children:"Pending Requests"}),(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User ID"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"Requested Models"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"Justification"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"Justification"})})]})]}),(0,l.jsx)(U.Z,{children:d.map(e=>(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)("p",{children:e.user_id})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)("p",{children:e.models[0]})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)("p",{children:e.justification})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)("p",{children:e.user_id})}),(0,l.jsx)(o.Z,{children:"Approve"}),(0,l.jsx)(o.Z,{variant:"secondary",className:"ml-2",children:"Deny"})]},e.request_id))})]})}):null]})})};let{Option:eo}=N.default;var ei=e=>{let{userID:t,accessToken:s}=e,[r]=I.Z.useForm(),[a,i]=(0,n.useState)(!1),[d,h]=(0,n.useState)(null),[u,x]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{let e=await p(s,t,"any"),l=[];for(let t=0;t{i(!1),r.resetFields()},g=()=>{i(!1),h(null),r.resetFields()},Z=async e=>{try{c.ZP.info("Making API Call"),i(!0),console.log("formValues in create user:",e);let l=await m(s,t,e);console.log("user create Response:",l),h(l.key),c.ZP.success("API user Created"),r.resetFields(),localStorage.removeItem("userData"+t)}catch(e){console.error("Error creating the user:",e)}};return(0,l.jsxs)("div",{children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>i(!0),children:"+ Create New User"}),(0,l.jsx)(C.Z,{title:"Create User",visible:a,width:800,footer:null,onOk:j,onCancel:g,children:(0,l.jsxs)(I.Z,{form:r,onFinish:Z,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsx)(I.Z.Item,{label:"User ID",name:"user_id",children:(0,l.jsx)(T.Z,{placeholder:"Enter User ID"})}),(0,l.jsx)(I.Z.Item,{label:"Team ID",name:"team_id",children:(0,l.jsx)(T.Z,{placeholder:"ai_team"})}),(0,l.jsx)(I.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(N.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:u.map(e=>(0,l.jsx)(eo,{value:e,children:e},e))})}),(0,l.jsx)(I.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(I.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(A.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(A.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Duration (eg: 30s, 30h, 30d)",name:"duration",children:(0,l.jsx)(T.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Metadata",name:"metadata",children:(0,l.jsx)(T.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(P.ZP,{htmlType:"submit",children:"Create User"})})]})}),d&&(0,l.jsxs)(C.Z,{title:"Save Your User",visible:a,onOk:j,onCancel:g,footer:null,children:[(0,l.jsxs)("p",{children:["Please save this secret user somewhere safe and accessible. For security reasons, ",(0,l.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret user, you will need to generate a new one."]}),(0,l.jsx)("p",{children:null!=d?"API user: ".concat(d):"User being created, this might take 30s"})]})]})},ec=e=>{let{accessToken:t,token:s,userRole:r,userID:a}=e,[o,i]=(0,n.useState)(null),[c,d]=(0,n.useState)([]);return((0,n.useEffect)(()=>{if(!t||!s||!r||!a)return;let e=async()=>{try{let e=await u(t,null,r,!0);console.log("user data response:",e),i(e)}catch(e){console.error("There was an error fetching the model data",e)}};t&&s&&r&&a&&e()},[t,s,r,a]),o&&t&&s&&r&&a)?(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(v.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(ei,{userID:a,accessToken:t}),(0,l.jsx)(F.Z,{children:(0,l.jsxs)(M.Z,{className:"mt-5",children:[(0,l.jsx)(z.Z,{children:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User ID "})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User Role"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User Models"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User Spend ($ USD)"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"User Max Budget ($ USD)"})})]})}),(0,l.jsx)(U.Z,{children:o.map(e=>(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.user_id})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.user_role?e.user_role:"app_user"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.models&&e.models.length>0?e.models:"All Models"})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.spend?e.spend:0})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:e.max_budget?e.max_budget:"Unlimited"})})]},e.user_id))})]})})]})}):(0,l.jsx)("div",{children:"Loading..."})},ed=s(8510),em=e=>{let{teams:t,searchParams:s,accessToken:r,setTeams:a}=e,[i]=I.Z.useForm(),[d]=I.Z.useForm(),{Title:m,Paragraph:h}=H.default,[u,x]=(0,n.useState)(""),[p,j]=(0,n.useState)(t?t[0]:null),[g,Z]=(0,n.useState)(!1),[y,f]=(0,n.useState)(!1),w=async e=>{try{if(null!=r){c.ZP.info("Making API Call");let s=await k(r,e);null!==t?a([...t,s]):a([s]),console.log("response for team create call: ".concat(s)),Z(!1)}}catch(e){console.error("Error creating the key:",e)}},E=async e=>{try{if(null!=r&&null!=t){c.ZP.info("Making API Call");let s={role:"user",user_email:e.user_email,user_id:e.user_id},l=await _(r,p.team_id,s);console.log("response for team create call: ".concat(l.data));let n=t.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(l.data.team_id)),e.team_id===l.data.team_id));if(console.log("foundIndex: ".concat(n)),-1!==n){let e=[...t];e[n]=l.data,a(e),j(l.data)}f(!1)}}catch(e){console.error("Error creating the key:",e)}};return console.log("received teams ".concat(t)),(0,l.jsx)("div",{className:"w-full",children:(0,l.jsxs)(v.Z,{numItems:1,className:"gap-2 p-2 h-[75vh] w-full",children:[(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(m,{level:4,children:"All Teams"}),(0,l.jsx)(F.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(z.Z,{children:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(q.Z,{children:"Team Name"}),(0,l.jsx)(q.Z,{children:"Spend (USD)"}),(0,l.jsx)(q.Z,{children:"Budget (USD)"}),(0,l.jsx)(q.Z,{children:"TPM / RPM Limits"})]})}),(0,l.jsx)(U.Z,{children:t&&t.length>0?t.map(e=>(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:e.team_alias}),(0,l.jsx)(L.Z,{children:e.spend}),(0,l.jsx)(L.Z,{children:e.max_budget?e.max_budget:"No limit"}),(0,l.jsx)(L.Z,{children:(0,l.jsxs)(S.Z,{children:["TPM Limit:"," ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,l.jsx)("br",{})," RPM Limit:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(R.Z,{icon:ed.Z,size:"sm"})})]},e.team_id)):null})]})})]}),(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto",onClick:()=>Z(!0),children:"+ Create New Team"}),(0,l.jsx)(C.Z,{title:"Create Team",visible:g,width:800,footer:null,onOk:()=>{Z(!1),i.resetFields()},onCancel:()=>{Z(!1),i.resetFields()},children:(0,l.jsxs)(I.Z,{form:i,onFinish:w,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(I.Z.Item,{label:"Team Name",name:"team_alias",children:(0,l.jsx)(T.Z,{})}),(0,l.jsx)(I.Z.Item,{label:"Models",name:"models",children:(0,l.jsx)(N.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"}})}),(0,l.jsx)(I.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,l.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,l.jsx)(I.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,l.jsx)(A.Z,{step:1,width:400})}),(0,l.jsx)(I.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,l.jsx)(A.Z,{step:1,width:400})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(P.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(m,{level:4,children:"Team Members"}),(0,l.jsx)(h,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),t&&t.length>0?(0,l.jsx)(X.Z,{defaultValue:"0",children:t.map((e,t)=>(0,l.jsx)(Q.Z,{value:String(t),onClick:()=>{j(e)},children:e.team_alias},t))}):(0,l.jsxs)(h,{children:["No team created. ",(0,l.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,l.jsx)(b.Z,{numColSpan:1,children:(0,l.jsx)(F.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,l.jsxs)(M.Z,{children:[(0,l.jsx)(z.Z,{children:(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(q.Z,{children:"Member Name"}),(0,l.jsx)(q.Z,{children:"Role"}),(0,l.jsx)(q.Z,{children:"Action"})]})}),(0,l.jsx)(U.Z,{children:p?p.members_with_roles.map((e,t)=>(0,l.jsxs)(B.Z,{children:[(0,l.jsx)(L.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,l.jsx)(L.Z,{children:e.role}),(0,l.jsx)(L.Z,{children:(0,l.jsx)(R.Z,{icon:ed.Z,size:"sm"})})]},t)):null})]})})}),(0,l.jsxs)(b.Z,{numColSpan:1,children:[(0,l.jsx)(o.Z,{className:"mx-auto mb-5",onClick:()=>f(!0),children:"+ Add member"}),(0,l.jsx)(C.Z,{title:"Add member",visible:y,width:800,footer:null,onOk:()=>{f(!1),d.resetFields()},onCancel:()=>{f(!1),d.resetFields()},children:(0,l.jsxs)(I.Z,{form:i,onFinish:E,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(I.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,l.jsx)(T.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,l.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,l.jsx)(I.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,l.jsx)(T.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,l.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,l.jsx)(P.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},eh=s(92836),eu=s(26734),ex=s(41608),ep=s(32126),ej=s(23682),eg=s(12968),eZ=s(67951);async function ey(e,t,s,l){console.log("isLocal:",!1);let n=window.location.origin,r=new eg.ZP.OpenAI({apiKey:l,baseURL:n,dangerouslyAllowBrowser:!0});for await(let l of(await r.chat.completions.create({model:s,stream:!0,messages:[{role:"user",content:e}]})))console.log(l),l.choices[0].delta.content&&t(l.choices[0].delta.content)}var ef=e=>{let{accessToken:t,token:s,userRole:r,userID:a}=e,[o,i]=(0,n.useState)(""),[c,d]=(0,n.useState)([]),[m,h]=(0,n.useState)(void 0),[u,x]=(0,n.useState)(null);(0,n.useEffect)(()=>{t&&s&&r&&a&&(async()=>{let e=await p(t,a,r);console.log("model_info:",e),(null==e?void 0:e.data.length)>0&&(x(e.data),h(e.data[0].id))})()},[t,a,r]);let j=(e,t)=>{d(s=>{let l=s[s.length-1];return l&&l.role===e?[...s.slice(0,s.length-1),{role:e,content:l.content+t}]:[...s,{role:e,content:t}]})},g=async()=>{if(""!==o.trim()&&t&&s&&r&&a){d(e=>[...e,{role:"user",content:o}]);try{m&&await ey(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,l.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,l.jsx)(v.Z,{className:"gap-2 p-10 h-[75vh] w-full",children:(0,l.jsx)(F.Z,{children:(0,l.jsxs)(eu.Z,{children:[(0,l.jsxs)(ex.Z,{className:"mt-4",children:[(0,l.jsx)(eh.Z,{children:"Chat"}),(0,l.jsx)(eh.Z,{children:"API Reference"})]}),(0,l.jsxs)(ej.Z,{children:[(0,l.jsxs)(ep.Z,{children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("label",{children:"Select Model:"}),(0,l.jsx)("select",{value:m||"",onChange:e=>h(e.target.value),children:null==u?void 0:u.map(e=>(0,l.jsx)("option",{value:e.id,children:e.id},e.id))})]}),(0,l.jsxs)(M.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,l.jsx)(z.Z,{children:(0,l.jsx)(B.Z,{children:(0,l.jsx)(L.Z,{children:(0,l.jsx)(G.Z,{children:"Chat"})})})}),(0,l.jsx)(U.Z,{children:c.map((e,t)=>(0,l.jsx)(B.Z,{children:(0,l.jsx)(L.Z,{children:"".concat(e.role,": ").concat(e.content)})},t))})]}),(0,l.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,l.jsxs)("div",{className:"flex",children:[(0,l.jsx)("input",{type:"text",value:o,onChange:e=>i(e.target.value),className:"flex-1 p-2 border rounded-md mr-2",placeholder:"Type your message..."}),(0,l.jsx)("button",{onClick:g,className:"p-2 bg-blue-500 text-white rounded-md",children:"Send"})]})})]}),(0,l.jsx)(ep.Z,{children:(0,l.jsxs)(eu.Z,{children:[(0,l.jsxs)(ex.Z,{children:[(0,l.jsx)(eh.Z,{children:"OpenAI Python SDK"}),(0,l.jsx)(eh.Z,{children:"LlamaIndex"}),(0,l.jsx)(eh.Z,{children:"Langchain Py"})]}),(0,l.jsxs)(ej.Z,{children:[(0,l.jsx)(ep.Z,{children:(0,l.jsx)(eZ.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="http://0.0.0.0:4000" # proxy base url\n)\n\nresponse = client.chat.completions.create(\n model="gpt-3.5-turbo", # model to use from Models Tab\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ],\n extra_body={\n "metadata": {\n "generation_name": "ishaan-generation-openai-client",\n "generation_id": "openai-client-gen-id22",\n "trace_id": "openai-client-trace-id22",\n "trace_user_id": "openai-client-user-id2"\n }\n }\n)\n\nprint(response)\n '})}),(0,l.jsx)(ep.Z,{children:(0,l.jsx)(eZ.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="azure-gpt-3.5", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="http://0.0.0.0:4000",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n '})}),(0,l.jsx)(ep.Z,{children:(0,l.jsx)(eZ.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 '})})]})]})})]})]})})})})},ew=s(33509),ek=s(30569);let{Sider:e_}=ew.default;var eb=e=>{let{setPage:t,userRole:s,defaultSelectedKey:n}=e;return(0,l.jsx)(ew.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,l.jsx)(e_,{width:120,children:(0,l.jsxs)(ek.Z,{mode:"inline",defaultSelectedKeys:n||["1"],style:{height:"100%",borderRight:0},children:[(0,l.jsx)(ek.Z.Item,{onClick:()=>t("api-keys"),children:"API Keys"},"1"),(0,l.jsx)(ek.Z.Item,{onClick:()=>t("models"),children:"Models"},"2"),(0,l.jsx)(ek.Z.Item,{onClick:()=>t("llm-playground"),children:"Chat UI"},"3"),(0,l.jsx)(ek.Z.Item,{onClick:()=>t("usage"),children:"Usage"},"4"),"Admin"==s?(0,l.jsx)(ek.Z.Item,{onClick:()=>t("users"),children:"Users"},"5"):null,"Admin"==s?(0,l.jsx)(ek.Z.Item,{onClick:()=>t("teams"),children:"Teams"},"6"):null]})})})};let ev=e=>{let{payload:t,active:s}=e;if(!s||!t)return null;let n=t[0].payload,r=n.startTime,a=Object.entries(n.models).map(e=>{let[t,s]=e;return[t,s]});a.sort((e,t)=>t[1]-e[1]);let o=a.slice(0,5);return(0,l.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,s]=e;return(0,l.jsx)("div",{className:"flex flex-1 space-x-10",children:(0,l.jsx)("div",{className:"p-2",children:(0,l.jsxs)("p",{className:"text-tremor-content text-xs",children:[t,":",(0,l.jsxs)("span",{className:"text-xs text-tremor-content-emphasis",children:[" ",s?s<.01?"<$0.01":s.toFixed(2):""]})]})})},t)})]})};var eS=e=>{let{accessToken:t,token:s,userRole:r,userID:a}=e,o=new Date,[i,c]=(0,n.useState)([]),[d,m]=(0,n.useState)([]),[h,u]=(0,n.useState)([]),x=new Date(o.getFullYear(),o.getMonth(),1),p=new Date(o.getFullYear(),o.getMonth()+1,0),j=f(x),y=f(p);function f(e){let t=e.getFullYear(),s=e.getMonth()+1,l=e.getDate();return"".concat(t,"-").concat(s<10?"0"+s:s,"-").concat(l<10?"0"+l:l)}return console.log("Start date is ".concat(j)),console.log("End date is ".concat(y)),(0,n.useEffect)(()=>{t&&s&&r&&a&&(async()=>{try{await g(t,s,r,a,j,y).then(async e=>{let s=(await Z(t,function(e){let t=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[s,l]=e;"spend"!==s&&"startTime"!==s&&"models"!==s&&"users"!==s&&t.push({key:s,spend:l})})}),t.sort((e,t)=>Number(t.spend)-Number(e.spend));let s=t.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(s[0]))),s}(e))).info.map(e=>({key:(e.key_name||e.key_alias||e.token).substring(0,7),spend:e.spend}));m(s),u(function(e){let t={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[s,l]=e;""!==s&&null!=s&&"None"!=s&&(t[s]||(t[s]=0),t[s]+=l)})});let s=Object.entries(t).map(e=>{let[t,s]=e;return{user_id:t,spend:s}});s.sort((e,t)=>t.spend-e.spend);let l=s.slice(0,5);return console.log("topKeys: ".concat(Object.values(l[0]))),l}(e)),c(e)})}catch(e){console.error("There was an error fetching the data",e)}})()},[t,s,r,a,j,y]),(0,l.jsx)("div",{style:{width:"100%"},children:(0,l.jsxs)(v.Z,{numItems:2,className:"gap-2 p-10 h-[75vh] w-full",children:[(0,l.jsx)(b.Z,{numColSpan:2,children:(0,l.jsxs)(F.Z,{children:[(0,l.jsx)(G.Z,{children:"Monthly Spend"}),(0,l.jsx)(K.Z,{data:i,index:"startTime",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5,customTooltip:ev})]})}),(0,l.jsx)(b.Z,{numColSpan:1,children:(0,l.jsxs)(F.Z,{children:[(0,l.jsx)(G.Z,{children:"Top API Keys"}),(0,l.jsx)(K.Z,{className:"mt-4 h-40",data:d,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,l.jsx)(b.Z,{numColSpan:1,children:(0,l.jsxs)(F.Z,{children:[(0,l.jsx)(G.Z,{children:"Top Users"}),(0,l.jsx)(K.Z,{className:"mt-4 h-40",data:h,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})})},eN=()=>{let[e,t]=(0,n.useState)(""),[s,a]=(0,n.useState)(null),[o,c]=(0,n.useState)(null),[d,m]=(0,n.useState)(!0),h=(0,r.useSearchParams)(),u=h.get("userID"),x=h.get("token"),[p,j]=(0,n.useState)("api-keys"),[g,Z]=(0,n.useState)(null);return(0,n.useEffect)(()=>{if(x){let e=(0,et.o)(x);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),Z(e.key),e.user_role){let s=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:",s),t(s)}else console.log("User role not defined");e.user_email?a(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?m("username_password"==e.login_method):console.log("User Email is not set ".concat(e))}}},[x]),(0,l.jsx)(n.Suspense,{fallback:(0,l.jsx)("div",{children:"Loading..."}),children:(0,l.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,l.jsx)(i,{userID:u,userRole:e,userEmail:s,showSSOBanner:d}),(0,l.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,l.jsx)(eb,{setPage:j,userRole:e,defaultSelectedKey:null}),"api-keys"==p?(0,l.jsx)(es,{userID:u,userRole:e,teams:o,setUserRole:t,userEmail:s,setUserEmail:a,setTeams:c}):"models"==p?(0,l.jsx)(ea,{userID:u,userRole:e,token:x,accessToken:g}):"llm-playground"==p?(0,l.jsx)(ef,{userID:u,userRole:e,token:x,accessToken:g}):"users"==p?(0,l.jsx)(ec,{userID:u,userRole:e,token:x,accessToken:g}):"teams"==p?(0,l.jsx)(em,{teams:o,setTeams:c,searchParams:h,accessToken:g}):(0,l.jsx)(eS,{userID:u,userRole:e,token:x,accessToken:g})]})]})})}}},function(e){e.O(0,[303,971,69,744],function(){return e(e.s=79615)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/eSwVwl_InIrhYtCAqDMKF/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/h6IXdBMiZG7ES547qg1M-/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/eSwVwl_InIrhYtCAqDMKF/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/h6IXdBMiZG7ES547qg1M-/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/eSwVwl_InIrhYtCAqDMKF/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/h6IXdBMiZG7ES547qg1M-/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/eSwVwl_InIrhYtCAqDMKF/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/h6IXdBMiZG7ES547qg1M-/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/index.html b/litellm/proxy/_experimental/out/index.html index 935323194c4..a1e9ec23f07 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 b732f427065..6f20aff67e7 100644 --- a/litellm/proxy/_experimental/out/index.txt +++ b/litellm/proxy/_experimental/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[24143,["303","static/chunks/303-d80f23087a9e6aec.js","931","static/chunks/app/page-d4fe4a48cbd3572c.js"],""] +3:I[24143,["303","static/chunks/303-d80f23087a9e6aec.js","931","static/chunks/app/page-cc9d300e3b13fc1b.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["eSwVwl_InIrhYtCAqDMKF",[[["",{"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/6920a121699cde9c.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["h6IXdBMiZG7ES547qg1M-",[[["",{"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/6920a121699cde9c.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] 6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"🚅 LiteLLM"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]] 1:null diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index c887351ef2c..5e03b5f7017 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -2315,7 +2315,6 @@ async def startup_event(): ### CHECK IF VIEW EXISTS ### if prisma_client is not None: create_view_response = await prisma_client.check_view_exists() - print(f"create_view_response: {create_view_response}") # noqa ### START BUDGET SCHEDULER ### if prisma_client is not None: @@ -4063,7 +4062,7 @@ async def global_spend_logs(): """ global prisma_client - sql_query = """SELECT * FROM "globalspendperdate";""" + sql_query = """SELECT * FROM "MonthlyGlobalSpend";""" response = await prisma_client.db.query_raw(query=sql_query) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index f814d609887..ec563bbf339 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -489,18 +489,20 @@ class PrismaClient: ) async def check_view_exists(self): """ - Checks if the LiteLLM_VerificationTokenView exists in the user's db. + Checks if the LiteLLM_VerificationTokenView and MonthlyGlobalSpend exists in the user's db. - This is used for getting the token + team data in user_api_key_auth + LiteLLM_VerificationTokenView: This view is used for getting the token + team data in user_api_key_auth + + MonthlyGlobalSpend: This view is used for the admin view to see global spend for this month If the view doesn't exist, one will be created. """ try: # Try to select one row from the view - await self.db.execute_raw( + await self.db.query_raw( """SELECT 1 FROM "LiteLLM_VerificationTokenView" LIMIT 1""" ) - return "LiteLLM_VerificationTokenView Exists!" + print("LiteLLM_VerificationTokenView Exists!") # noqa except Exception as e: # If an error occurs, the view does not exist, so create it value = await self.health_check() @@ -518,7 +520,29 @@ class PrismaClient: """ ) - return "LiteLLM_VerificationTokenView Created!" + print("LiteLLM_VerificationTokenView Created!") # noqa + + try: + await self.db.query_raw("""SELECT 1 FROM "MonthlyGlobalSpend" LIMIT 1""") + print("MonthlyGlobalSpend Exists!") # noqa + except Exception as e: + sql_query = """ + CREATE OR REPLACE VIEW "MonthlyGlobalSpend" AS + SELECT + DATE("startTime") AS date, + SUM("spend") AS spend + FROM + "LiteLLM_SpendLogs" + WHERE + "startTime" >= (CURRENT_DATE - INTERVAL '30 days') + GROUP BY + DATE("startTime"); + """ + await self.db.execute_raw(query=sql_query) + + print("MonthlyGlobalSpend Created!") # noqa + + return @backoff.on_exception( backoff.expo, diff --git a/ui/litellm-dashboard/out/404.html b/ui/litellm-dashboard/out/404.html index 554dcf93ae5..687a52941c0 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/index.html b/ui/litellm-dashboard/out/index.html index 935323194c4..a1e9ec23f07 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 b732f427065..6f20aff67e7 100644 --- a/ui/litellm-dashboard/out/index.txt +++ b/ui/litellm-dashboard/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[24143,["303","static/chunks/303-d80f23087a9e6aec.js","931","static/chunks/app/page-d4fe4a48cbd3572c.js"],""] +3:I[24143,["303","static/chunks/303-d80f23087a9e6aec.js","931","static/chunks/app/page-cc9d300e3b13fc1b.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["eSwVwl_InIrhYtCAqDMKF",[[["",{"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/6920a121699cde9c.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["h6IXdBMiZG7ES547qg1M-",[[["",{"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/6920a121699cde9c.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 e77d3419c11c993ef276491ce0b169ecaab4e822 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 13:13:59 -0800 Subject: [PATCH 03/12] test: skip test with expired token --- litellm/tests/test_bedrock_completion.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/tests/test_bedrock_completion.py b/litellm/tests/test_bedrock_completion.py index d672ca78011..5ac0cb43c7d 100644 --- a/litellm/tests/test_bedrock_completion.py +++ b/litellm/tests/test_bedrock_completion.py @@ -149,6 +149,7 @@ def test_completion_bedrock_claude_external_client_auth(): # test_completion_bedrock_claude_external_client_auth() +@pytest.mark.skip(reason="Expired token, need to renew") def test_completion_bedrock_claude_sts_client_auth(): print("\ncalling bedrock claude external client auth") import os From dccfdc241b61c1d8e83ff9f06e219acde48f6e8d Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 13:26:08 -0800 Subject: [PATCH 04/12] refactor(test_bedrock_completion.py): clean test --- litellm/tests/test_bedrock_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/tests/test_bedrock_completion.py b/litellm/tests/test_bedrock_completion.py index 5ac0cb43c7d..7df1fcc485f 100644 --- a/litellm/tests/test_bedrock_completion.py +++ b/litellm/tests/test_bedrock_completion.py @@ -203,7 +203,7 @@ def test_completion_bedrock_claude_sts_client_auth(): pytest.fail(f"Error occurred: {e}") -test_completion_bedrock_claude_sts_client_auth() +# test_completion_bedrock_claude_sts_client_auth() def test_provisioned_throughput(): From 4c951d20bca8dfeb74c411a8ae5d1916240e28fb Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 13:46:20 -0800 Subject: [PATCH 05/12] test: removing aws tests - account suspended - pending their approval --- litellm/tests/test_amazing_s3_logs.py | 413 +++---- litellm/tests/test_async_fn.py | 1 + litellm/tests/test_bedrock_completion.py | 420 +++---- litellm/tests/test_caching.py | 1 + litellm/tests/test_completion.py | 7 + litellm/tests/test_completion_cost.py | 1 + litellm/tests/test_custom_callback_input.py | 4 + litellm/tests/test_custom_logger.py | 1 + litellm/tests/test_embedding.py | 3 + litellm/tests/test_exceptions.py | 7 +- litellm/tests/test_health_check.py | 1 + litellm/tests/test_image_generation.py | 2 + litellm/tests/test_key_generate_dynamodb.py | 1029 +++++++++-------- litellm/tests/test_model_max_token_adjust.py | 1 + .../tests/test_provider_specific_config.py | 2 + litellm/tests/test_router.py | 19 +- litellm/tests/test_router_timeout.py | 1 + litellm/tests/test_streaming.py | 4 + 18 files changed, 966 insertions(+), 951 deletions(-) diff --git a/litellm/tests/test_amazing_s3_logs.py b/litellm/tests/test_amazing_s3_logs.py index 7b8eb4a47d7..74d6eb5b945 100644 --- a/litellm/tests/test_amazing_s3_logs.py +++ b/litellm/tests/test_amazing_s3_logs.py @@ -1,253 +1,254 @@ -import sys -import os -import io, asyncio +## @pytest.mark.skip(reason="AWS Suspended Account") +# import sys +# import os +# import io, asyncio -# import logging -# logging.basicConfig(level=logging.DEBUG) -sys.path.insert(0, os.path.abspath("../..")) +# # import logging +# # logging.basicConfig(level=logging.DEBUG) +# sys.path.insert(0, os.path.abspath("../..")) -from litellm import completion -import litellm +# from litellm import completion +# import litellm -litellm.num_retries = 3 +# litellm.num_retries = 3 -import time, random -import pytest +# import time, random +# import pytest -def test_s3_logging(): - # all s3 requests need to be in one test function - # since we are modifying stdout, and pytests runs tests in parallel - # on circle ci - we only test litellm.acompletion() - try: - # redirect stdout to log_file - litellm.cache = litellm.Cache( - type="s3", s3_bucket_name="cache-bucket-litellm", s3_region_name="us-west-2" - ) +# def test_s3_logging(): +# # all s3 requests need to be in one test function +# # since we are modifying stdout, and pytests runs tests in parallel +# # on circle ci - we only test litellm.acompletion() +# try: +# # redirect stdout to log_file +# litellm.cache = litellm.Cache( +# type="s3", s3_bucket_name="cache-bucket-litellm", s3_region_name="us-west-2" +# ) - litellm.success_callback = ["s3"] - litellm.s3_callback_params = { - "s3_bucket_name": "litellm-logs", - "s3_aws_secret_access_key": "os.environ/AWS_SECRET_ACCESS_KEY", - "s3_aws_access_key_id": "os.environ/AWS_ACCESS_KEY_ID", - } - litellm.set_verbose = True +# litellm.success_callback = ["s3"] +# litellm.s3_callback_params = { +# "s3_bucket_name": "litellm-logs", +# "s3_aws_secret_access_key": "os.environ/AWS_SECRET_ACCESS_KEY", +# "s3_aws_access_key_id": "os.environ/AWS_ACCESS_KEY_ID", +# } +# litellm.set_verbose = True - print("Testing async s3 logging") +# print("Testing async s3 logging") - expected_keys = [] +# expected_keys = [] - import time +# import time - curr_time = str(time.time()) +# curr_time = str(time.time()) - async def _test(): - return await litellm.acompletion( - model="gpt-3.5-turbo", - messages=[{"role": "user", "content": f"This is a test {curr_time}"}], - max_tokens=10, - temperature=0.7, - user="ishaan-2", - ) +# async def _test(): +# return await litellm.acompletion( +# model="gpt-3.5-turbo", +# messages=[{"role": "user", "content": f"This is a test {curr_time}"}], +# max_tokens=10, +# temperature=0.7, +# user="ishaan-2", +# ) - response = asyncio.run(_test()) - print(f"response: {response}") - expected_keys.append(response.id) +# response = asyncio.run(_test()) +# print(f"response: {response}") +# expected_keys.append(response.id) - async def _test(): - return await litellm.acompletion( - model="gpt-3.5-turbo", - messages=[{"role": "user", "content": f"This is a test {curr_time}"}], - max_tokens=10, - temperature=0.7, - user="ishaan-2", - ) +# async def _test(): +# return await litellm.acompletion( +# model="gpt-3.5-turbo", +# messages=[{"role": "user", "content": f"This is a test {curr_time}"}], +# max_tokens=10, +# temperature=0.7, +# user="ishaan-2", +# ) - response = asyncio.run(_test()) - expected_keys.append(response.id) - print(f"response: {response}") - time.sleep(5) # wait 5s for logs to land +# response = asyncio.run(_test()) +# expected_keys.append(response.id) +# print(f"response: {response}") +# time.sleep(5) # wait 5s for logs to land - import boto3 +# import boto3 - s3 = boto3.client("s3") - bucket_name = "litellm-logs" - # List objects in the bucket - response = s3.list_objects(Bucket=bucket_name) +# s3 = boto3.client("s3") +# bucket_name = "litellm-logs" +# # List objects in the bucket +# response = s3.list_objects(Bucket=bucket_name) - # Sort the objects based on the LastModified timestamp - objects = sorted( - response["Contents"], key=lambda x: x["LastModified"], reverse=True - ) - # Get the keys of the most recent objects - most_recent_keys = [obj["Key"] for obj in objects] - print(most_recent_keys) - # for each key, get the part before "-" as the key. Do it safely - cleaned_keys = [] - for key in most_recent_keys: - split_key = key.split("_") - if len(split_key) < 2: - continue - cleaned_keys.append(split_key[1]) - print("\n most recent keys", most_recent_keys) - print("\n cleaned keys", cleaned_keys) - print("\n Expected keys: ", expected_keys) - matches = 0 - for key in expected_keys: - key += ".json" - assert key in cleaned_keys +# # Sort the objects based on the LastModified timestamp +# objects = sorted( +# response["Contents"], key=lambda x: x["LastModified"], reverse=True +# ) +# # Get the keys of the most recent objects +# most_recent_keys = [obj["Key"] for obj in objects] +# print(most_recent_keys) +# # for each key, get the part before "-" as the key. Do it safely +# cleaned_keys = [] +# for key in most_recent_keys: +# split_key = key.split("_") +# if len(split_key) < 2: +# continue +# cleaned_keys.append(split_key[1]) +# print("\n most recent keys", most_recent_keys) +# print("\n cleaned keys", cleaned_keys) +# print("\n Expected keys: ", expected_keys) +# matches = 0 +# for key in expected_keys: +# key += ".json" +# assert key in cleaned_keys - if key in cleaned_keys: - matches += 1 - # remove the match key - cleaned_keys.remove(key) - # this asserts we log, the first request + the 2nd cached request - print("we had two matches ! passed ", matches) - assert matches == 2 - try: - # cleanup s3 bucket in test - for key in most_recent_keys: - s3.delete_object(Bucket=bucket_name, Key=key) - except: - # don't let cleanup fail a test - pass - except Exception as e: - pytest.fail(f"An exception occurred - {e}") - finally: - # post, close log file and verify - # Reset stdout to the original value - print("Passed! Testing async s3 logging") +# if key in cleaned_keys: +# matches += 1 +# # remove the match key +# cleaned_keys.remove(key) +# # this asserts we log, the first request + the 2nd cached request +# print("we had two matches ! passed ", matches) +# assert matches == 2 +# try: +# # cleanup s3 bucket in test +# for key in most_recent_keys: +# s3.delete_object(Bucket=bucket_name, Key=key) +# except: +# # don't let cleanup fail a test +# pass +# except Exception as e: +# pytest.fail(f"An exception occurred - {e}") +# finally: +# # post, close log file and verify +# # Reset stdout to the original value +# print("Passed! Testing async s3 logging") -# test_s3_logging() +# # test_s3_logging() -def test_s3_logging_async(): - # this tests time added to make s3 logging calls, vs just acompletion calls - try: - litellm.set_verbose = True - # Make 5 calls with an empty success_callback - litellm.success_callback = [] - start_time_empty_callback = asyncio.run(make_async_calls()) - print("done with no callback test") +# def test_s3_logging_async(): +# # this tests time added to make s3 logging calls, vs just acompletion calls +# try: +# litellm.set_verbose = True +# # Make 5 calls with an empty success_callback +# litellm.success_callback = [] +# start_time_empty_callback = asyncio.run(make_async_calls()) +# print("done with no callback test") - print("starting s3 logging load test") - # Make 5 calls with success_callback set to "langfuse" - litellm.success_callback = ["s3"] - litellm.s3_callback_params = { - "s3_bucket_name": "litellm-logs", - "s3_aws_secret_access_key": "os.environ/AWS_SECRET_ACCESS_KEY", - "s3_aws_access_key_id": "os.environ/AWS_ACCESS_KEY_ID", - } - start_time_s3 = asyncio.run(make_async_calls()) - print("done with s3 test") +# print("starting s3 logging load test") +# # Make 5 calls with success_callback set to "langfuse" +# litellm.success_callback = ["s3"] +# litellm.s3_callback_params = { +# "s3_bucket_name": "litellm-logs", +# "s3_aws_secret_access_key": "os.environ/AWS_SECRET_ACCESS_KEY", +# "s3_aws_access_key_id": "os.environ/AWS_ACCESS_KEY_ID", +# } +# start_time_s3 = asyncio.run(make_async_calls()) +# print("done with s3 test") - # Compare the time for both scenarios - print(f"Time taken with success_callback='s3': {start_time_s3}") - print(f"Time taken with empty success_callback: {start_time_empty_callback}") +# # Compare the time for both scenarios +# print(f"Time taken with success_callback='s3': {start_time_s3}") +# print(f"Time taken with empty success_callback: {start_time_empty_callback}") - # assert the diff is not more than 1 second - assert abs(start_time_s3 - start_time_empty_callback) < 1 +# # assert the diff is not more than 1 second +# assert abs(start_time_s3 - start_time_empty_callback) < 1 - except litellm.Timeout as e: - pass - except Exception as e: - pytest.fail(f"An exception occurred - {e}") +# except litellm.Timeout as e: +# pass +# except Exception as e: +# pytest.fail(f"An exception occurred - {e}") -async def make_async_calls(): - tasks = [] - for _ in range(5): - task = asyncio.create_task( - litellm.acompletion( - model="azure/chatgpt-v-2", - messages=[{"role": "user", "content": "This is a test"}], - max_tokens=5, - temperature=0.7, - timeout=5, - user="langfuse_latency_test_user", - mock_response="It's simple to use and easy to get started", - ) - ) - tasks.append(task) +# async def make_async_calls(): +# tasks = [] +# for _ in range(5): +# task = asyncio.create_task( +# litellm.acompletion( +# model="azure/chatgpt-v-2", +# messages=[{"role": "user", "content": "This is a test"}], +# max_tokens=5, +# temperature=0.7, +# timeout=5, +# user="langfuse_latency_test_user", +# mock_response="It's simple to use and easy to get started", +# ) +# ) +# tasks.append(task) - # Measure the start time before running the tasks - start_time = asyncio.get_event_loop().time() +# # Measure the start time before running the tasks +# start_time = asyncio.get_event_loop().time() - # Wait for all tasks to complete - responses = await asyncio.gather(*tasks) +# # Wait for all tasks to complete +# responses = await asyncio.gather(*tasks) - # Print the responses when tasks return - for idx, response in enumerate(responses): - print(f"Response from Task {idx + 1}: {response}") +# # Print the responses when tasks return +# for idx, response in enumerate(responses): +# print(f"Response from Task {idx + 1}: {response}") - # Calculate the total time taken - total_time = asyncio.get_event_loop().time() - start_time +# # Calculate the total time taken +# total_time = asyncio.get_event_loop().time() - start_time - return total_time +# return total_time -def test_s3_logging_r2(): - # all s3 requests need to be in one test function - # since we are modifying stdout, and pytests runs tests in parallel - # on circle ci - we only test litellm.acompletion() - try: - # redirect stdout to log_file - # litellm.cache = litellm.Cache( - # type="s3", s3_bucket_name="litellm-r2-bucket", s3_region_name="us-west-2" - # ) - litellm.set_verbose = True - from litellm._logging import verbose_logger - import logging +# def test_s3_logging_r2(): +# # all s3 requests need to be in one test function +# # since we are modifying stdout, and pytests runs tests in parallel +# # on circle ci - we only test litellm.acompletion() +# try: +# # redirect stdout to log_file +# # litellm.cache = litellm.Cache( +# # type="s3", s3_bucket_name="litellm-r2-bucket", s3_region_name="us-west-2" +# # ) +# litellm.set_verbose = True +# from litellm._logging import verbose_logger +# import logging - verbose_logger.setLevel(level=logging.DEBUG) +# verbose_logger.setLevel(level=logging.DEBUG) - litellm.success_callback = ["s3"] - litellm.s3_callback_params = { - "s3_bucket_name": "litellm-r2-bucket", - "s3_aws_secret_access_key": "os.environ/R2_S3_ACCESS_KEY", - "s3_aws_access_key_id": "os.environ/R2_S3_ACCESS_ID", - "s3_endpoint_url": "os.environ/R2_S3_URL", - "s3_region_name": "os.environ/R2_S3_REGION_NAME", - } - print("Testing async s3 logging") +# litellm.success_callback = ["s3"] +# litellm.s3_callback_params = { +# "s3_bucket_name": "litellm-r2-bucket", +# "s3_aws_secret_access_key": "os.environ/R2_S3_ACCESS_KEY", +# "s3_aws_access_key_id": "os.environ/R2_S3_ACCESS_ID", +# "s3_endpoint_url": "os.environ/R2_S3_URL", +# "s3_region_name": "os.environ/R2_S3_REGION_NAME", +# } +# print("Testing async s3 logging") - expected_keys = [] +# expected_keys = [] - import time +# import time - curr_time = str(time.time()) +# curr_time = str(time.time()) - async def _test(): - return await litellm.acompletion( - model="gpt-3.5-turbo", - messages=[{"role": "user", "content": f"This is a test {curr_time}"}], - max_tokens=10, - temperature=0.7, - user="ishaan-2", - ) +# async def _test(): +# return await litellm.acompletion( +# model="gpt-3.5-turbo", +# messages=[{"role": "user", "content": f"This is a test {curr_time}"}], +# max_tokens=10, +# temperature=0.7, +# user="ishaan-2", +# ) - response = asyncio.run(_test()) - print(f"response: {response}") - expected_keys.append(response.id) +# response = asyncio.run(_test()) +# print(f"response: {response}") +# expected_keys.append(response.id) - import boto3 +# import boto3 - s3 = boto3.client( - "s3", - endpoint_url=os.getenv("R2_S3_URL"), - region_name=os.getenv("R2_S3_REGION_NAME"), - aws_access_key_id=os.getenv("R2_S3_ACCESS_ID"), - aws_secret_access_key=os.getenv("R2_S3_ACCESS_KEY"), - ) +# s3 = boto3.client( +# "s3", +# endpoint_url=os.getenv("R2_S3_URL"), +# region_name=os.getenv("R2_S3_REGION_NAME"), +# aws_access_key_id=os.getenv("R2_S3_ACCESS_ID"), +# aws_secret_access_key=os.getenv("R2_S3_ACCESS_KEY"), +# ) - bucket_name = "litellm-r2-bucket" - # List objects in the bucket - response = s3.list_objects(Bucket=bucket_name) +# bucket_name = "litellm-r2-bucket" +# # List objects in the bucket +# response = s3.list_objects(Bucket=bucket_name) - except Exception as e: - pytest.fail(f"An exception occurred - {e}") - finally: - # post, close log file and verify - # Reset stdout to the original value - print("Passed! Testing async s3 logging") +# except Exception as e: +# pytest.fail(f"An exception occurred - {e}") +# finally: +# # post, close log file and verify +# # Reset stdout to the original value +# print("Passed! Testing async s3 logging") diff --git a/litellm/tests/test_async_fn.py b/litellm/tests/test_async_fn.py index 86cbfafbf13..d6ff9aa872f 100644 --- a/litellm/tests/test_async_fn.py +++ b/litellm/tests/test_async_fn.py @@ -203,6 +203,7 @@ async def test_hf_completion_tgi(): # test_get_cloudflare_response_streaming() +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_completion_sagemaker(): # litellm.set_verbose=True diff --git a/litellm/tests/test_bedrock_completion.py b/litellm/tests/test_bedrock_completion.py index 7df1fcc485f..6b31c8a0611 100644 --- a/litellm/tests/test_bedrock_completion.py +++ b/litellm/tests/test_bedrock_completion.py @@ -1,258 +1,258 @@ -import sys, os -import traceback -from dotenv import load_dotenv +# import sys, os +# import traceback +# from dotenv import load_dotenv -load_dotenv() -import os, io +# load_dotenv() +# import os, io -sys.path.insert( - 0, os.path.abspath("../..") -) # Adds the parent directory to the system path -import pytest -import litellm -from litellm import embedding, completion, completion_cost, Timeout -from litellm import RateLimitError +# sys.path.insert( +# 0, os.path.abspath("../..") +# ) # Adds the parent directory to the system path +# import pytest +# import litellm +# from litellm import embedding, completion, completion_cost, Timeout +# from litellm import RateLimitError -# litellm.num_retries = 3 -litellm.cache = None -litellm.success_callback = [] -user_message = "Write a short poem about the sky" -messages = [{"content": user_message, "role": "user"}] +# # litellm.num_retries = 3 +# litellm.cache = None +# litellm.success_callback = [] +# user_message = "Write a short poem about the sky" +# messages = [{"content": user_message, "role": "user"}] -@pytest.fixture(autouse=True) -def reset_callbacks(): - print("\npytest fixture - resetting callbacks") - litellm.success_callback = [] - litellm._async_success_callback = [] - litellm.failure_callback = [] - litellm.callbacks = [] +# @pytest.fixture(autouse=True) +# def reset_callbacks(): +# print("\npytest fixture - resetting callbacks") +# litellm.success_callback = [] +# litellm._async_success_callback = [] +# litellm.failure_callback = [] +# litellm.callbacks = [] -def test_completion_bedrock_claude_completion_auth(): - print("calling bedrock claude completion params auth") - import os +# def test_completion_bedrock_claude_completion_auth(): +# print("calling bedrock claude completion params auth") +# import os - aws_access_key_id = os.environ["AWS_ACCESS_KEY_ID"] - aws_secret_access_key = os.environ["AWS_SECRET_ACCESS_KEY"] - aws_region_name = os.environ["AWS_REGION_NAME"] +# aws_access_key_id = os.environ["AWS_ACCESS_KEY_ID"] +# aws_secret_access_key = os.environ["AWS_SECRET_ACCESS_KEY"] +# aws_region_name = os.environ["AWS_REGION_NAME"] - os.environ.pop("AWS_ACCESS_KEY_ID", None) - os.environ.pop("AWS_SECRET_ACCESS_KEY", None) - os.environ.pop("AWS_REGION_NAME", None) +# os.environ.pop("AWS_ACCESS_KEY_ID", None) +# os.environ.pop("AWS_SECRET_ACCESS_KEY", None) +# os.environ.pop("AWS_REGION_NAME", None) - try: - response = completion( - model="bedrock/anthropic.claude-instant-v1", - messages=messages, - max_tokens=10, - temperature=0.1, - aws_access_key_id=aws_access_key_id, - aws_secret_access_key=aws_secret_access_key, - aws_region_name=aws_region_name, - ) - # Add any assertions here to check the response - print(response) +# try: +# response = completion( +# model="bedrock/anthropic.claude-instant-v1", +# messages=messages, +# max_tokens=10, +# temperature=0.1, +# aws_access_key_id=aws_access_key_id, +# aws_secret_access_key=aws_secret_access_key, +# aws_region_name=aws_region_name, +# ) +# # Add any assertions here to check the response +# print(response) - os.environ["AWS_ACCESS_KEY_ID"] = aws_access_key_id - os.environ["AWS_SECRET_ACCESS_KEY"] = aws_secret_access_key - os.environ["AWS_REGION_NAME"] = aws_region_name - except RateLimitError: - pass - except Exception as e: - pytest.fail(f"Error occurred: {e}") +# os.environ["AWS_ACCESS_KEY_ID"] = aws_access_key_id +# os.environ["AWS_SECRET_ACCESS_KEY"] = aws_secret_access_key +# os.environ["AWS_REGION_NAME"] = aws_region_name +# except RateLimitError: +# pass +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") -# test_completion_bedrock_claude_completion_auth() +# # test_completion_bedrock_claude_completion_auth() -def test_completion_bedrock_claude_2_1_completion_auth(): - print("calling bedrock claude 2.1 completion params auth") - import os +# def test_completion_bedrock_claude_2_1_completion_auth(): +# print("calling bedrock claude 2.1 completion params auth") +# import os - aws_access_key_id = os.environ["AWS_ACCESS_KEY_ID"] - aws_secret_access_key = os.environ["AWS_SECRET_ACCESS_KEY"] - aws_region_name = os.environ["AWS_REGION_NAME"] +# aws_access_key_id = os.environ["AWS_ACCESS_KEY_ID"] +# aws_secret_access_key = os.environ["AWS_SECRET_ACCESS_KEY"] +# aws_region_name = os.environ["AWS_REGION_NAME"] - os.environ.pop("AWS_ACCESS_KEY_ID", None) - os.environ.pop("AWS_SECRET_ACCESS_KEY", None) - os.environ.pop("AWS_REGION_NAME", None) - try: - response = completion( - model="bedrock/anthropic.claude-v2:1", - messages=messages, - max_tokens=10, - temperature=0.1, - aws_access_key_id=aws_access_key_id, - aws_secret_access_key=aws_secret_access_key, - aws_region_name=aws_region_name, - ) - # Add any assertions here to check the response - print(response) +# os.environ.pop("AWS_ACCESS_KEY_ID", None) +# os.environ.pop("AWS_SECRET_ACCESS_KEY", None) +# os.environ.pop("AWS_REGION_NAME", None) +# try: +# response = completion( +# model="bedrock/anthropic.claude-v2:1", +# messages=messages, +# max_tokens=10, +# temperature=0.1, +# aws_access_key_id=aws_access_key_id, +# aws_secret_access_key=aws_secret_access_key, +# aws_region_name=aws_region_name, +# ) +# # Add any assertions here to check the response +# print(response) - os.environ["AWS_ACCESS_KEY_ID"] = aws_access_key_id - os.environ["AWS_SECRET_ACCESS_KEY"] = aws_secret_access_key - os.environ["AWS_REGION_NAME"] = aws_region_name - except RateLimitError: - pass - except Exception as e: - pytest.fail(f"Error occurred: {e}") +# os.environ["AWS_ACCESS_KEY_ID"] = aws_access_key_id +# os.environ["AWS_SECRET_ACCESS_KEY"] = aws_secret_access_key +# os.environ["AWS_REGION_NAME"] = aws_region_name +# except RateLimitError: +# pass +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") -# test_completion_bedrock_claude_2_1_completion_auth() +# # test_completion_bedrock_claude_2_1_completion_auth() -def test_completion_bedrock_claude_external_client_auth(): - print("\ncalling bedrock claude external client auth") - import os +# def test_completion_bedrock_claude_external_client_auth(): +# print("\ncalling bedrock claude external client auth") +# import os - aws_access_key_id = os.environ["AWS_ACCESS_KEY_ID"] - aws_secret_access_key = os.environ["AWS_SECRET_ACCESS_KEY"] - aws_region_name = os.environ["AWS_REGION_NAME"] +# aws_access_key_id = os.environ["AWS_ACCESS_KEY_ID"] +# aws_secret_access_key = os.environ["AWS_SECRET_ACCESS_KEY"] +# aws_region_name = os.environ["AWS_REGION_NAME"] - os.environ.pop("AWS_ACCESS_KEY_ID", None) - os.environ.pop("AWS_SECRET_ACCESS_KEY", None) - os.environ.pop("AWS_REGION_NAME", None) +# os.environ.pop("AWS_ACCESS_KEY_ID", None) +# os.environ.pop("AWS_SECRET_ACCESS_KEY", None) +# os.environ.pop("AWS_REGION_NAME", None) - try: - import boto3 +# try: +# import boto3 - litellm.set_verbose = True +# litellm.set_verbose = True - bedrock = boto3.client( - service_name="bedrock-runtime", - region_name=aws_region_name, - aws_access_key_id=aws_access_key_id, - aws_secret_access_key=aws_secret_access_key, - endpoint_url=f"https://bedrock-runtime.{aws_region_name}.amazonaws.com", - ) +# bedrock = boto3.client( +# service_name="bedrock-runtime", +# region_name=aws_region_name, +# aws_access_key_id=aws_access_key_id, +# aws_secret_access_key=aws_secret_access_key, +# endpoint_url=f"https://bedrock-runtime.{aws_region_name}.amazonaws.com", +# ) - response = completion( - model="bedrock/anthropic.claude-instant-v1", - messages=messages, - max_tokens=10, - temperature=0.1, - aws_bedrock_client=bedrock, - ) - # Add any assertions here to check the response - print(response) +# response = completion( +# model="bedrock/anthropic.claude-instant-v1", +# messages=messages, +# max_tokens=10, +# temperature=0.1, +# aws_bedrock_client=bedrock, +# ) +# # Add any assertions here to check the response +# print(response) - os.environ["AWS_ACCESS_KEY_ID"] = aws_access_key_id - os.environ["AWS_SECRET_ACCESS_KEY"] = aws_secret_access_key - os.environ["AWS_REGION_NAME"] = aws_region_name - except RateLimitError: - pass - except Exception as e: - pytest.fail(f"Error occurred: {e}") +# os.environ["AWS_ACCESS_KEY_ID"] = aws_access_key_id +# os.environ["AWS_SECRET_ACCESS_KEY"] = aws_secret_access_key +# os.environ["AWS_REGION_NAME"] = aws_region_name +# except RateLimitError: +# pass +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") -# test_completion_bedrock_claude_external_client_auth() +# # test_completion_bedrock_claude_external_client_auth() -@pytest.mark.skip(reason="Expired token, need to renew") -def test_completion_bedrock_claude_sts_client_auth(): - print("\ncalling bedrock claude external client auth") - import os +# @pytest.mark.skip(reason="Expired token, need to renew") +# def test_completion_bedrock_claude_sts_client_auth(): +# print("\ncalling bedrock claude external client auth") +# import os - aws_access_key_id = os.environ["AWS_TEMP_ACCESS_KEY_ID"] - aws_secret_access_key = os.environ["AWS_TEMP_SECRET_ACCESS_KEY"] - aws_region_name = os.environ["AWS_REGION_NAME"] - aws_role_name = os.environ["AWS_TEMP_ROLE_NAME"] +# aws_access_key_id = os.environ["AWS_TEMP_ACCESS_KEY_ID"] +# aws_secret_access_key = os.environ["AWS_TEMP_SECRET_ACCESS_KEY"] +# aws_region_name = os.environ["AWS_REGION_NAME"] +# aws_role_name = os.environ["AWS_TEMP_ROLE_NAME"] - try: - import boto3 +# try: +# import boto3 - litellm.set_verbose = True +# litellm.set_verbose = True - response = completion( - model="bedrock/anthropic.claude-instant-v1", - messages=messages, - max_tokens=10, - temperature=0.1, - aws_region_name=aws_region_name, - aws_access_key_id=aws_access_key_id, - aws_secret_access_key=aws_secret_access_key, - aws_role_name=aws_role_name, - aws_session_name="my-test-session", - ) +# response = completion( +# model="bedrock/anthropic.claude-instant-v1", +# messages=messages, +# max_tokens=10, +# temperature=0.1, +# aws_region_name=aws_region_name, +# aws_access_key_id=aws_access_key_id, +# aws_secret_access_key=aws_secret_access_key, +# aws_role_name=aws_role_name, +# aws_session_name="my-test-session", +# ) - response = embedding( - model="cohere.embed-multilingual-v3", - input=["hello world"], - aws_region_name="us-east-1", - aws_access_key_id=aws_access_key_id, - aws_secret_access_key=aws_secret_access_key, - aws_role_name=aws_role_name, - aws_session_name="my-test-session", - ) +# response = embedding( +# model="cohere.embed-multilingual-v3", +# input=["hello world"], +# aws_region_name="us-east-1", +# aws_access_key_id=aws_access_key_id, +# aws_secret_access_key=aws_secret_access_key, +# aws_role_name=aws_role_name, +# aws_session_name="my-test-session", +# ) - response = completion( - model="gpt-3.5-turbo", - messages=messages, - aws_region_name="us-east-1", - aws_access_key_id=aws_access_key_id, - aws_secret_access_key=aws_secret_access_key, - aws_role_name=aws_role_name, - aws_session_name="my-test-session", - ) - # Add any assertions here to check the response - print(response) - except RateLimitError: - pass - except Exception as e: - pytest.fail(f"Error occurred: {e}") +# response = completion( +# model="gpt-3.5-turbo", +# messages=messages, +# aws_region_name="us-east-1", +# aws_access_key_id=aws_access_key_id, +# aws_secret_access_key=aws_secret_access_key, +# aws_role_name=aws_role_name, +# aws_session_name="my-test-session", +# ) +# # Add any assertions here to check the response +# print(response) +# except RateLimitError: +# pass +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") -# test_completion_bedrock_claude_sts_client_auth() +# # test_completion_bedrock_claude_sts_client_auth() -def test_provisioned_throughput(): - try: - litellm.set_verbose = True - import botocore, json, io - import botocore.session - from botocore.stub import Stubber +# def test_provisioned_throughput(): +# try: +# litellm.set_verbose = True +# import botocore, json, io +# import botocore.session +# from botocore.stub import Stubber - bedrock_client = botocore.session.get_session().create_client( - "bedrock-runtime", region_name="us-east-1" - ) +# bedrock_client = botocore.session.get_session().create_client( +# "bedrock-runtime", region_name="us-east-1" +# ) - expected_params = { - "accept": "application/json", - "body": '{"prompt": "\\n\\nHuman: Hello, how are you?\\n\\nAssistant: ", ' - '"max_tokens_to_sample": 256}', - "contentType": "application/json", - "modelId": "provisioned-model-arn", - } - response_from_bedrock = { - "body": io.StringIO( - json.dumps( - { - "completion": " Here is a short poem about the sky:", - "stop_reason": "max_tokens", - "stop": None, - } - ) - ), - "contentType": "contentType", - "ResponseMetadata": {"HTTPStatusCode": 200}, - } +# expected_params = { +# "accept": "application/json", +# "body": '{"prompt": "\\n\\nHuman: Hello, how are you?\\n\\nAssistant: ", ' +# '"max_tokens_to_sample": 256}', +# "contentType": "application/json", +# "modelId": "provisioned-model-arn", +# } +# response_from_bedrock = { +# "body": io.StringIO( +# json.dumps( +# { +# "completion": " Here is a short poem about the sky:", +# "stop_reason": "max_tokens", +# "stop": None, +# } +# ) +# ), +# "contentType": "contentType", +# "ResponseMetadata": {"HTTPStatusCode": 200}, +# } - with Stubber(bedrock_client) as stubber: - stubber.add_response( - "invoke_model", - service_response=response_from_bedrock, - expected_params=expected_params, - ) - response = litellm.completion( - model="bedrock/anthropic.claude-instant-v1", - model_id="provisioned-model-arn", - messages=[{"content": "Hello, how are you?", "role": "user"}], - aws_bedrock_client=bedrock_client, - ) - print("response stubbed", response) - except Exception as e: - pytest.fail(f"Error occurred: {e}") +# with Stubber(bedrock_client) as stubber: +# stubber.add_response( +# "invoke_model", +# service_response=response_from_bedrock, +# expected_params=expected_params, +# ) +# response = litellm.completion( +# model="bedrock/anthropic.claude-instant-v1", +# model_id="provisioned-model-arn", +# messages=[{"content": "Hello, how are you?", "role": "user"}], +# aws_bedrock_client=bedrock_client, +# ) +# print("response stubbed", response) +# except Exception as e: +# pytest.fail(f"Error occurred: {e}") -# test_provisioned_throughput() +# # test_provisioned_throughput() diff --git a/litellm/tests/test_caching.py b/litellm/tests/test_caching.py index de9740ebdfa..1764b65c046 100644 --- a/litellm/tests/test_caching.py +++ b/litellm/tests/test_caching.py @@ -546,6 +546,7 @@ def test_redis_cache_acompletion_stream(): # test_redis_cache_acompletion_stream() +@pytest.mark.skip(reason="AWS Suspended Account") def test_redis_cache_acompletion_stream_bedrock(): import asyncio diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index 71f77c9b49d..f502e5f0388 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -1404,6 +1404,7 @@ def test_customprompt_together_ai(): # test_customprompt_together_ai() +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_sagemaker(): try: litellm.set_verbose = True @@ -1429,6 +1430,7 @@ def test_completion_sagemaker(): # test_completion_sagemaker() +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_sagemaker_stream(): try: litellm.set_verbose = False @@ -1459,6 +1461,7 @@ def test_completion_sagemaker_stream(): pytest.fail(f"Error occurred: {e}") +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_chat_sagemaker(): try: messages = [{"role": "user", "content": "Hey, how's it going?"}] @@ -1483,6 +1486,7 @@ def test_completion_chat_sagemaker(): # test_completion_chat_sagemaker() +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_chat_sagemaker_mistral(): try: messages = [{"role": "user", "content": "Hey, how's it going?"}] @@ -1501,6 +1505,7 @@ def test_completion_chat_sagemaker_mistral(): # test_completion_chat_sagemaker_mistral() +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_bedrock_titan_null_response(): try: response = completion( @@ -1526,6 +1531,7 @@ def test_completion_bedrock_titan_null_response(): pytest.fail(f"An error occurred - {str(e)}") +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_bedrock_titan(): try: response = completion( @@ -1568,6 +1574,7 @@ def test_completion_bedrock_claude(): # test_completion_bedrock_claude() +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_bedrock_cohere(): print("calling bedrock cohere") litellm.set_verbose = True diff --git a/litellm/tests/test_completion_cost.py b/litellm/tests/test_completion_cost.py index bb460b76bd9..b82cfd0a623 100644 --- a/litellm/tests/test_completion_cost.py +++ b/litellm/tests/test_completion_cost.py @@ -171,6 +171,7 @@ def test_cost_openai_image_gen(): assert cost == 0.019922944 +@pytest.mark.skip(reason="AWS Suspended Account") def test_cost_bedrock_pricing(): """ - get pricing specific to region for a model diff --git a/litellm/tests/test_custom_callback_input.py b/litellm/tests/test_custom_callback_input.py index 6e55cc5a1e5..683173b21e7 100644 --- a/litellm/tests/test_custom_callback_input.py +++ b/litellm/tests/test_custom_callback_input.py @@ -478,6 +478,7 @@ async def test_async_chat_azure_stream(): ## Test Bedrock + sync +@pytest.mark.skip(reason="AWS Suspended Account") def test_chat_bedrock_stream(): try: customHandler = CompletionCustomHandler() @@ -518,6 +519,7 @@ def test_chat_bedrock_stream(): ## Test Bedrock + Async +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_async_chat_bedrock_stream(): try: @@ -561,6 +563,7 @@ async def test_async_chat_bedrock_stream(): ## Test Sagemaker + Async +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_async_chat_sagemaker_stream(): try: @@ -793,6 +796,7 @@ async def test_async_embedding_azure(): ## Test Bedrock + Async +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_async_embedding_bedrock(): try: diff --git a/litellm/tests/test_custom_logger.py b/litellm/tests/test_custom_logger.py index a7b0c937f0a..fe130768906 100644 --- a/litellm/tests/test_custom_logger.py +++ b/litellm/tests/test_custom_logger.py @@ -388,6 +388,7 @@ async def test_async_custom_handler_embedding_optional_param(): # asyncio.run(test_async_custom_handler_embedding_optional_param()) +@pytest.mark.skip(reason="AWS Account suspended. Pending their approval") @pytest.mark.asyncio async def test_async_custom_handler_embedding_optional_param_bedrock(): """ diff --git a/litellm/tests/test_embedding.py b/litellm/tests/test_embedding.py index 4637a79e044..b88f6ae7f4f 100644 --- a/litellm/tests/test_embedding.py +++ b/litellm/tests/test_embedding.py @@ -256,6 +256,7 @@ async def test_vertexai_aembedding(): pytest.fail(f"Error occurred: {e}") +@pytest.mark.skip(reason="AWS Suspended Account") def test_bedrock_embedding_titan(): try: # this tests if we support str input for bedrock embedding @@ -422,6 +423,7 @@ def test_aembedding_azure(): # test_aembedding_azure() +@pytest.mark.skip(reason="AWS Suspended Account") def test_sagemaker_embeddings(): try: response = litellm.embedding( @@ -438,6 +440,7 @@ def test_sagemaker_embeddings(): pytest.fail(f"Error occurred: {e}") +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_sagemaker_aembeddings(): try: diff --git a/litellm/tests/test_exceptions.py b/litellm/tests/test_exceptions.py index 4729cabb235..9c90014c0d6 100644 --- a/litellm/tests/test_exceptions.py +++ b/litellm/tests/test_exceptions.py @@ -42,6 +42,7 @@ exception_models = [ # Test 1: Context Window Errors +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.parametrize("model", exception_models) def test_context_window(model): print("Testing context window error") @@ -120,9 +121,9 @@ def invalid_auth(model): # set the model key to an invalid key, depending on th os.environ["AI21_API_KEY"] = "bad-key" elif "togethercomputer" in model: temporary_key = os.environ["TOGETHERAI_API_KEY"] - os.environ[ - "TOGETHERAI_API_KEY" - ] = "84060c79880fc49df126d3e87b53f8a463ff6e1c6d27fe64207cde25cdfcd1f24a" + os.environ["TOGETHERAI_API_KEY"] = ( + "84060c79880fc49df126d3e87b53f8a463ff6e1c6d27fe64207cde25cdfcd1f24a" + ) elif model in litellm.openrouter_models: temporary_key = os.environ["OPENROUTER_API_KEY"] os.environ["OPENROUTER_API_KEY"] = "bad-key" diff --git a/litellm/tests/test_health_check.py b/litellm/tests/test_health_check.py index 21b72d2ac39..f632e769216 100644 --- a/litellm/tests/test_health_check.py +++ b/litellm/tests/test_health_check.py @@ -87,6 +87,7 @@ async def test_azure_img_gen_health_check(): # asyncio.run(test_azure_img_gen_health_check()) +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_sagemaker_embedding_health_check(): response = await litellm.ahealth_check( diff --git a/litellm/tests/test_image_generation.py b/litellm/tests/test_image_generation.py index 59ccaacd8d8..0672319a21a 100644 --- a/litellm/tests/test_image_generation.py +++ b/litellm/tests/test_image_generation.py @@ -121,6 +121,7 @@ async def test_async_image_generation_azure(): pytest.fail(f"An exception occurred - {str(e)}") +@pytest.mark.skip(reason="AWS Suspended Account") def test_image_generation_bedrock(): try: litellm.set_verbose = True @@ -141,6 +142,7 @@ def test_image_generation_bedrock(): pytest.fail(f"An exception occurred - {str(e)}") +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_aimage_generation_bedrock_with_optional_params(): try: diff --git a/litellm/tests/test_key_generate_dynamodb.py b/litellm/tests/test_key_generate_dynamodb.py index 573bd944db6..e28b9f047fa 100644 --- a/litellm/tests/test_key_generate_dynamodb.py +++ b/litellm/tests/test_key_generate_dynamodb.py @@ -1,516 +1,517 @@ -# Test the following scenarios: -# 1. Generate a Key, and use it to make a call -# 2. Make a call with invalid key, expect it to fail -# 3. Make a call to a key with invalid model - expect to fail -# 4. Make a call to a key with valid model - expect to pass -# 5. Make a call with key over budget, expect to fail -# 6. Make a streaming chat/completions call with key over budget, expect to fail - - -# function to call to generate key - async def new_user(data: NewUserRequest): -# function to validate a request - async def user_auth(request: Request): - -import sys, os -import traceback -from dotenv import load_dotenv -from fastapi import Request - -load_dotenv() -import os, io - -# this file is to test litellm/proxy - -sys.path.insert( - 0, os.path.abspath("../..") -) # Adds the parent directory to the system path -import pytest, logging, asyncio -import litellm, asyncio -from litellm.proxy.proxy_server import ( - new_user, - user_api_key_auth, - user_update, - generate_key_fn, -) - -from litellm.proxy._types import NewUserRequest, DynamoDBArgs, GenerateKeyRequest -from litellm.proxy.utils import DBClient, hash_token -from starlette.datastructures import URL - - -request_data = { - "model": "azure-gpt-3.5", - "messages": [ - {"role": "user", "content": "this is my new test. respond in 50 lines"} - ], -} - - -@pytest.fixture -def custom_db_client(): - # Assuming DBClient is a class that needs to be instantiated - db_args = { - "ssl_verify": False, - "billing_mode": "PAY_PER_REQUEST", - "region_name": "us-west-2", - } - custom_db_client = DBClient( - custom_db_type="dynamo_db", - custom_db_args=db_args, - ) - # Reset litellm.proxy.proxy_server.prisma_client to None - litellm.proxy.proxy_server.prisma_client = None - - return custom_db_client - - -def test_generate_and_call_with_valid_key(custom_db_client): - # 1. Generate a Key, and use it to make a call - setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) - setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") - try: - - async def test(): - request = NewUserRequest() - key = await new_user(request) - print(key) - - generated_key = key.key - bearer_token = "Bearer " + generated_key - - request = Request(scope={"type": "http"}) - request._url = URL(url="/chat/completions") - - # 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.mark.skip(reason="AWS Suspended Account") +# # Test the following scenarios: +# # 1. Generate a Key, and use it to make a call +# # 2. Make a call with invalid key, expect it to fail +# # 3. Make a call to a key with invalid model - expect to fail +# # 4. Make a call to a key with valid model - expect to pass +# # 5. Make a call with key over budget, expect to fail +# # 6. Make a streaming chat/completions call with key over budget, expect to fail + + +# # function to call to generate key - async def new_user(data: NewUserRequest): +# # function to validate a request - async def user_auth(request: Request): + +# import sys, os +# import traceback +# from dotenv import load_dotenv +# from fastapi import Request + +# load_dotenv() +# import os, io + +# # this file is to test litellm/proxy + +# sys.path.insert( +# 0, os.path.abspath("../..") +# ) # Adds the parent directory to the system path +# import pytest, logging, asyncio +# import litellm, asyncio +# from litellm.proxy.proxy_server import ( +# new_user, +# user_api_key_auth, +# user_update, +# generate_key_fn, +# ) + +# from litellm.proxy._types import NewUserRequest, DynamoDBArgs, GenerateKeyRequest +# from litellm.proxy.utils import DBClient, hash_token +# from starlette.datastructures import URL + + +# request_data = { +# "model": "azure-gpt-3.5", +# "messages": [ +# {"role": "user", "content": "this is my new test. respond in 50 lines"} +# ], +# } + + +# @pytest.fixture +# def custom_db_client(): +# # Assuming DBClient is a class that needs to be instantiated +# db_args = { +# "ssl_verify": False, +# "billing_mode": "PAY_PER_REQUEST", +# "region_name": "us-west-2", +# } +# custom_db_client = DBClient( +# custom_db_type="dynamo_db", +# custom_db_args=db_args, +# ) +# # Reset litellm.proxy.proxy_server.prisma_client to None +# litellm.proxy.proxy_server.prisma_client = None + +# return custom_db_client + + +# def test_generate_and_call_with_valid_key(custom_db_client): +# # 1. Generate a Key, and use it to make a call +# setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) +# setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") +# try: + +# async def test(): +# request = NewUserRequest() +# key = await new_user(request) +# print(key) + +# generated_key = key.key +# bearer_token = "Bearer " + generated_key + +# request = Request(scope={"type": "http"}) +# request._url = URL(url="/chat/completions") + +# # 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) - asyncio.run(test()) - except Exception as e: - pytest.fail(f"An exception occurred - {str(e)}") - - -def test_call_with_invalid_key(custom_db_client): - # 2. Make a call with invalid key, expect it to fail - setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) - setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") - try: - - async def test(): - generated_key = "bad-key" - bearer_token = "Bearer " + generated_key - - request = Request(scope={"type": "http"}, receive=None) - request._url = URL(url="/chat/completions") - - # use generated key to auth in - result = await user_api_key_auth(request=request, api_key=bearer_token) - pytest.fail(f"This should have failed!. IT's an invalid key") +# asyncio.run(test()) +# except Exception as e: +# pytest.fail(f"An exception occurred - {str(e)}") + + +# def test_call_with_invalid_key(custom_db_client): +# # 2. Make a call with invalid key, expect it to fail +# setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) +# setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") +# try: + +# async def test(): +# generated_key = "bad-key" +# bearer_token = "Bearer " + generated_key + +# request = Request(scope={"type": "http"}, receive=None) +# request._url = URL(url="/chat/completions") + +# # use generated key to auth in +# result = await user_api_key_auth(request=request, api_key=bearer_token) +# pytest.fail(f"This should have failed!. IT's an invalid key") - asyncio.run(test()) - except Exception as e: - print("Got Exception", e) - print(e.message) - assert "Authentication Error" in e.message - pass - - -def test_call_with_invalid_model(custom_db_client): - # 3. Make a call to a key with an invalid model - expect to fail - from litellm._logging import verbose_proxy_logger - import logging - - verbose_proxy_logger.setLevel(logging.DEBUG) - setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) - setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") - try: - - async def test(): - request = NewUserRequest(models=["mistral"]) - key = await new_user(request) - print(key) - - generated_key = key.key - bearer_token = "Bearer " + generated_key - - request = Request(scope={"type": "http"}) - request._url = URL(url="/chat/completions") - - async def return_body(): - return b'{"model": "gemini-pro-vision"}' - - request.body = return_body - - # use generated key to auth in - result = await user_api_key_auth(request=request, api_key=bearer_token) - pytest.fail(f"This should have failed!. IT's an invalid model") - - asyncio.run(test()) - except Exception as e: - assert ( - e.message - == "Authentication Error, API Key not allowed to access model. This token can only access models=['mistral']. Tried to access gemini-pro-vision" - ) - pass - - -def test_call_with_valid_model(custom_db_client): - # 4. Make a call to a key with a valid model - expect to pass - setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) - setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") - try: - - async def test(): - request = NewUserRequest(models=["mistral"]) - key = await new_user(request) - print(key) - - generated_key = key.key - bearer_token = "Bearer " + generated_key - - request = Request(scope={"type": "http"}) - request._url = URL(url="/chat/completions") - - async def return_body(): - return b'{"model": "mistral"}' - - 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) - - asyncio.run(test()) - except Exception as e: - pytest.fail(f"An exception occurred - {str(e)}") - - -def test_call_with_user_over_budget(custom_db_client): - # 5. Make a call with a key over budget, expect to fail - setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) - setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") - from litellm._logging import verbose_proxy_logger, verbose_logger - import logging - - litellm.set_verbose = True - verbose_logger.setLevel(logging.DEBUG) - verbose_proxy_logger.setLevel(logging.DEBUG) - try: - - async def test(): - request = NewUserRequest(max_budget=0.00001) - key = await new_user(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") - - # 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 - - resp = ModelResponse( - id="chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac", - 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={ - "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, - ) - await asyncio.sleep(5) - # 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: - error_detail = e.message - assert "Authentication Error, ExceededBudget:" in error_detail - print(vars(e)) - - -def test_call_with_user_over_budget_stream(custom_db_client): - # 6. Make a call with a key over budget, expect to fail - setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) - setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") - from litellm._logging import verbose_proxy_logger - import logging - - litellm.set_verbose = True - verbose_proxy_logger.setLevel(logging.DEBUG) - try: - - async def test(): - request = NewUserRequest(max_budget=0.00001) - key = await new_user(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") - - # 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 - - resp = ModelResponse( - id="chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac", - 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={ - "stream": True, - "complete_streaming_response": resp, - "litellm_params": { - "metadata": { - "user_api_key": hash_token(generated_key), - "user_api_key_user_id": user_id, - } - }, - "response_cost": 0.00002, - }, - completion_response=ModelResponse(), - ) - await asyncio.sleep(5) - # 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: - error_detail = e.message - assert "Authentication Error, ExceededBudget:" in error_detail - print(vars(e)) - - -def test_call_with_user_key_budget(custom_db_client): - # 7. Make a call with a key over budget, expect to fail - setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) - setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") - from litellm._logging import verbose_proxy_logger - import logging - - verbose_proxy_logger.setLevel(logging.DEBUG) - try: - - async def test(): - request = GenerateKeyRequest(max_budget=0.00001) - 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") - - # 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 - - resp = ModelResponse( - id="chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac", - 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={ - "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, - ) - await asyncio.sleep(5) - # 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: - error_detail = e.message - assert "Authentication Error, ExceededTokenBudget:" in error_detail - print(vars(e)) - - -def test_call_with_key_over_budget_stream(custom_db_client): - # 8. Make a call with a key over budget, expect to fail - setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) - setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") - from litellm._logging import verbose_proxy_logger - import logging - - litellm.set_verbose = True - verbose_proxy_logger.setLevel(logging.DEBUG) - try: - - async def test(): - request = GenerateKeyRequest(max_budget=0.00001) - 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") - - # 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 - - resp = ModelResponse( - id="chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac", - 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={ - "stream": True, - "complete_streaming_response": resp, - "litellm_params": { - "metadata": { - "user_api_key": hash_token(generated_key), - "user_api_key_user_id": user_id, - } - }, - "response_cost": 0.00002, - }, - completion_response=ModelResponse(), - ) - await asyncio.sleep(5) - # 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: - error_detail = e.message - assert "Authentication Error, ExceededTokenBudget:" in error_detail - print(vars(e)) - - -def test_dynamo_db_migration(custom_db_client): - # Tests the temporary patch we have in place - setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) - setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") - setattr(litellm.proxy.proxy_server, "user_custom_auth", None) - try: - - async def test(): - request = GenerateKeyRequest(max_budget=1) - key = await generate_key_fn(request) - print(key) - - generated_key = key.key - bearer_token = ( - "Bearer " + generated_key - ) # this works with ishaan's db, it's a never expiring key - - request = Request(scope={"type": "http"}) - request._url = URL(url="/chat/completions") - - async def return_body(): - return b'{"model": "azure-models"}' - - 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) - - asyncio.run(test()) - except Exception as e: - pytest.fail(f"An exception occurred - {traceback.format_exc()}") +# asyncio.run(test()) +# except Exception as e: +# print("Got Exception", e) +# print(e.message) +# assert "Authentication Error" in e.message +# pass + + +# def test_call_with_invalid_model(custom_db_client): +# # 3. Make a call to a key with an invalid model - expect to fail +# from litellm._logging import verbose_proxy_logger +# import logging + +# verbose_proxy_logger.setLevel(logging.DEBUG) +# setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) +# setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") +# try: + +# async def test(): +# request = NewUserRequest(models=["mistral"]) +# key = await new_user(request) +# print(key) + +# generated_key = key.key +# bearer_token = "Bearer " + generated_key + +# request = Request(scope={"type": "http"}) +# request._url = URL(url="/chat/completions") + +# async def return_body(): +# return b'{"model": "gemini-pro-vision"}' + +# request.body = return_body + +# # use generated key to auth in +# result = await user_api_key_auth(request=request, api_key=bearer_token) +# pytest.fail(f"This should have failed!. IT's an invalid model") + +# asyncio.run(test()) +# except Exception as e: +# assert ( +# e.message +# == "Authentication Error, API Key not allowed to access model. This token can only access models=['mistral']. Tried to access gemini-pro-vision" +# ) +# pass + + +# def test_call_with_valid_model(custom_db_client): +# # 4. Make a call to a key with a valid model - expect to pass +# setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) +# setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") +# try: + +# async def test(): +# request = NewUserRequest(models=["mistral"]) +# key = await new_user(request) +# print(key) + +# generated_key = key.key +# bearer_token = "Bearer " + generated_key + +# request = Request(scope={"type": "http"}) +# request._url = URL(url="/chat/completions") + +# async def return_body(): +# return b'{"model": "mistral"}' + +# 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) + +# asyncio.run(test()) +# except Exception as e: +# pytest.fail(f"An exception occurred - {str(e)}") + + +# def test_call_with_user_over_budget(custom_db_client): +# # 5. Make a call with a key over budget, expect to fail +# setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) +# setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") +# from litellm._logging import verbose_proxy_logger, verbose_logger +# import logging + +# litellm.set_verbose = True +# verbose_logger.setLevel(logging.DEBUG) +# verbose_proxy_logger.setLevel(logging.DEBUG) +# try: + +# async def test(): +# request = NewUserRequest(max_budget=0.00001) +# key = await new_user(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") + +# # 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 + +# resp = ModelResponse( +# id="chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac", +# 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={ +# "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, +# ) +# await asyncio.sleep(5) +# # 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: +# error_detail = e.message +# assert "Authentication Error, ExceededBudget:" in error_detail +# print(vars(e)) + + +# def test_call_with_user_over_budget_stream(custom_db_client): +# # 6. Make a call with a key over budget, expect to fail +# setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) +# setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") +# from litellm._logging import verbose_proxy_logger +# import logging + +# litellm.set_verbose = True +# verbose_proxy_logger.setLevel(logging.DEBUG) +# try: + +# async def test(): +# request = NewUserRequest(max_budget=0.00001) +# key = await new_user(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") + +# # 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 + +# resp = ModelResponse( +# id="chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac", +# 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={ +# "stream": True, +# "complete_streaming_response": resp, +# "litellm_params": { +# "metadata": { +# "user_api_key": hash_token(generated_key), +# "user_api_key_user_id": user_id, +# } +# }, +# "response_cost": 0.00002, +# }, +# completion_response=ModelResponse(), +# ) +# await asyncio.sleep(5) +# # 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: +# error_detail = e.message +# assert "Authentication Error, ExceededBudget:" in error_detail +# print(vars(e)) + + +# def test_call_with_user_key_budget(custom_db_client): +# # 7. Make a call with a key over budget, expect to fail +# setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) +# setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") +# from litellm._logging import verbose_proxy_logger +# import logging + +# verbose_proxy_logger.setLevel(logging.DEBUG) +# try: + +# async def test(): +# request = GenerateKeyRequest(max_budget=0.00001) +# 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") + +# # 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 + +# resp = ModelResponse( +# id="chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac", +# 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={ +# "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, +# ) +# await asyncio.sleep(5) +# # 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: +# error_detail = e.message +# assert "Authentication Error, ExceededTokenBudget:" in error_detail +# print(vars(e)) + + +# def test_call_with_key_over_budget_stream(custom_db_client): +# # 8. Make a call with a key over budget, expect to fail +# setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) +# setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") +# from litellm._logging import verbose_proxy_logger +# import logging + +# litellm.set_verbose = True +# verbose_proxy_logger.setLevel(logging.DEBUG) +# try: + +# async def test(): +# request = GenerateKeyRequest(max_budget=0.00001) +# 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") + +# # 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 + +# resp = ModelResponse( +# id="chatcmpl-e41836bb-bb8b-4df2-8e70-8f3e160155ac", +# 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={ +# "stream": True, +# "complete_streaming_response": resp, +# "litellm_params": { +# "metadata": { +# "user_api_key": hash_token(generated_key), +# "user_api_key_user_id": user_id, +# } +# }, +# "response_cost": 0.00002, +# }, +# completion_response=ModelResponse(), +# ) +# await asyncio.sleep(5) +# # 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: +# error_detail = e.message +# assert "Authentication Error, ExceededTokenBudget:" in error_detail +# print(vars(e)) + + +# def test_dynamo_db_migration(custom_db_client): +# # Tests the temporary patch we have in place +# setattr(litellm.proxy.proxy_server, "custom_db_client", custom_db_client) +# setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") +# setattr(litellm.proxy.proxy_server, "user_custom_auth", None) +# try: + +# async def test(): +# request = GenerateKeyRequest(max_budget=1) +# key = await generate_key_fn(request) +# print(key) + +# generated_key = key.key +# bearer_token = ( +# "Bearer " + generated_key +# ) # this works with ishaan's db, it's a never expiring key + +# request = Request(scope={"type": "http"}) +# request._url = URL(url="/chat/completions") + +# async def return_body(): +# return b'{"model": "azure-models"}' + +# 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) + +# asyncio.run(test()) +# except Exception as e: +# pytest.fail(f"An exception occurred - {traceback.format_exc()}") diff --git a/litellm/tests/test_model_max_token_adjust.py b/litellm/tests/test_model_max_token_adjust.py index b4d48b5e28e..e6b31245f03 100644 --- a/litellm/tests/test_model_max_token_adjust.py +++ b/litellm/tests/test_model_max_token_adjust.py @@ -12,6 +12,7 @@ import litellm from litellm import completion +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_sagemaker(): litellm.set_verbose = True litellm.drop_params = True diff --git a/litellm/tests/test_provider_specific_config.py b/litellm/tests/test_provider_specific_config.py index 6c0edf02bd1..5e5d19c7862 100644 --- a/litellm/tests/test_provider_specific_config.py +++ b/litellm/tests/test_provider_specific_config.py @@ -473,6 +473,7 @@ def aleph_alpha_test_completion(): # Sagemaker +@pytest.mark.skip(reason="AWS Suspended Account") def sagemaker_test_completion(): litellm.SagemakerConfig(max_new_tokens=10) # litellm.set_verbose=True @@ -514,6 +515,7 @@ def sagemaker_test_completion(): # Bedrock +@pytest.mark.skip(reason="AWS Suspended Account") def bedrock_test_completion(): litellm.AmazonCohereConfig(max_tokens=10) # litellm.set_verbose=True diff --git a/litellm/tests/test_router.py b/litellm/tests/test_router.py index ab329e14ae7..127caf223ba 100644 --- a/litellm/tests/test_router.py +++ b/litellm/tests/test_router.py @@ -166,14 +166,6 @@ def test_call_one_endpoint(): "tpm": 240000, "rpm": 1800, }, - { - "model_name": "claude-v1", - "litellm_params": { - "model": "bedrock/anthropic.claude-instant-v1", - }, - "tpm": 100000, - "rpm": 10000, - }, { "model_name": "text-embedding-ada-002", "litellm_params": { @@ -202,15 +194,6 @@ def test_call_one_endpoint(): ) print("\n response", response) - async def call_bedrock_claude(): - response = await router.acompletion( - model="bedrock/anthropic.claude-instant-v1", - messages=[{"role": "user", "content": "hello this request will pass"}], - specific_deployment=True, - ) - - print("\n response", response) - async def call_azure_embedding(): response = await router.aembedding( model="azure/azure-embedding-model", @@ -221,7 +204,6 @@ def test_call_one_endpoint(): print("\n response", response) asyncio.run(call_azure_completion()) - asyncio.run(call_bedrock_claude()) asyncio.run(call_azure_embedding()) os.environ["AZURE_API_BASE"] = old_api_base @@ -593,6 +575,7 @@ def test_azure_embedding_on_router(): # test_azure_embedding_on_router() +@pytest.mark.skip(reason="AWS Suspended Account") def test_bedrock_on_router(): litellm.set_verbose = True print("\n Testing bedrock on router\n") diff --git a/litellm/tests/test_router_timeout.py b/litellm/tests/test_router_timeout.py index b22683c41e1..139914f6dff 100644 --- a/litellm/tests/test_router_timeout.py +++ b/litellm/tests/test_router_timeout.py @@ -87,6 +87,7 @@ def test_router_timeouts(): print("********** TOKENS USED SO FAR = ", total_tokens_used) +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_router_timeouts_bedrock(): import openai diff --git a/litellm/tests/test_streaming.py b/litellm/tests/test_streaming.py index 7fdd5020487..86f5bcf35d2 100644 --- a/litellm/tests/test_streaming.py +++ b/litellm/tests/test_streaming.py @@ -764,6 +764,7 @@ def test_completion_replicate_stream_bad_key(): # test_completion_replicate_stream_bad_key() +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_bedrock_claude_stream(): try: litellm.set_verbose = False @@ -810,6 +811,7 @@ def test_completion_bedrock_claude_stream(): # test_completion_bedrock_claude_stream() +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_bedrock_ai21_stream(): try: litellm.set_verbose = False @@ -911,6 +913,7 @@ def test_sagemaker_weird_response(): # test_sagemaker_weird_response() +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_sagemaker_streaming_async(): try: @@ -949,6 +952,7 @@ async def test_sagemaker_streaming_async(): # asyncio.run(test_sagemaker_streaming_async()) +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_sagemaker_stream(): try: response = completion( From 9ec8e33a5a8b82aa529155be4b519cac473592ea Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 14:27:01 -0800 Subject: [PATCH 06/12] test: skip aws test - aws account suspended --- litellm/tests/test_bedrock_completion.py | 1 + litellm/tests/test_proxy_server.py | 1 + 2 files changed, 2 insertions(+) diff --git a/litellm/tests/test_bedrock_completion.py b/litellm/tests/test_bedrock_completion.py index 6b31c8a0611..a448fc3a57f 100644 --- a/litellm/tests/test_bedrock_completion.py +++ b/litellm/tests/test_bedrock_completion.py @@ -1,3 +1,4 @@ +# @pytest.mark.skip(reason="AWS Suspended Account") # import sys, os # import traceback # from dotenv import load_dotenv diff --git a/litellm/tests/test_proxy_server.py b/litellm/tests/test_proxy_server.py index 70fef0e064e..caf32299f21 100644 --- a/litellm/tests/test_proxy_server.py +++ b/litellm/tests/test_proxy_server.py @@ -125,6 +125,7 @@ def test_embedding(client_no_auth): pytest.fail(f"LiteLLM Proxy test failed. Exception - {str(e)}") +@pytest.mark.skip(reason="AWS Suspended Account") def test_bedrock_embedding(client_no_auth): global headers from litellm.proxy.proxy_server import user_custom_auth From e5e973f0bb4b6d9b2c9b243de47c3b3e8cce8df1 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 14:33:03 -0800 Subject: [PATCH 07/12] fix(utils.py): fix palm exception mapping --- litellm/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/utils.py b/litellm/utils.py index acad6170200..4114a8cd162 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -6951,7 +6951,7 @@ def exception_type( if "500 An internal error has occurred." in error_str: exception_mapping_worked = True raise APIError( - status_code=original_exception.status_code, + status_code=getattr(original_exception, "status_code", 500), message=f"PalmException - {original_exception.message}", llm_provider="palm", model=model, From 8a038e7da49d0de196db7973415973af7ae3b8ea Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 14:42:50 -0800 Subject: [PATCH 08/12] test: skip aws test - aws account suspended --- litellm/tests/test_embedding.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/tests/test_embedding.py b/litellm/tests/test_embedding.py index b88f6ae7f4f..2c9de496c47 100644 --- a/litellm/tests/test_embedding.py +++ b/litellm/tests/test_embedding.py @@ -302,6 +302,7 @@ def test_bedrock_embedding_titan(): # test_bedrock_embedding_titan() +@pytest.mark.skip(reason="AWS Suspended Account") def test_bedrock_embedding_cohere(): try: litellm.set_verbose = False From 6cff6535d2a57594682cbf6f93811fbd98f875cd Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 14:55:57 -0800 Subject: [PATCH 09/12] test(test_proxy_server_caching.py): skip aws test - aws account suspended --- litellm/tests/test_proxy_server_caching.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/tests/test_proxy_server_caching.py b/litellm/tests/test_proxy_server_caching.py index a9cf3504e43..d6f98d27b46 100644 --- a/litellm/tests/test_proxy_server_caching.py +++ b/litellm/tests/test_proxy_server_caching.py @@ -61,6 +61,7 @@ def generate_random_word(length=4): return "".join(random.choice(letters) for _ in range(length)) +@pytest.mark.skip(reason="AWS Suspended Account") def test_chat_completion(client_no_auth): global headers try: From f7a2d3faef4cd6b950c0f02848da82b868853c32 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 15:00:53 -0800 Subject: [PATCH 10/12] test: skip sagemaker test - aws account suspended --- tests/test_keys.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_keys.py b/tests/test_keys.py index c2b9571805e..a6e85d6d9c0 100644 --- a/tests/test_keys.py +++ b/tests/test_keys.py @@ -490,6 +490,7 @@ async def test_key_crossing_budget(): assert "ExceededTokenBudget: Current spend for token:" in str(e) +@pytest.mark.skip(reason="AWS Suspended Account") @pytest.mark.asyncio async def test_key_info_spend_values_sagemaker(): """ From d9862520bc7f01ffd13e8de8b61bd088e35d6794 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 15:37:48 -0800 Subject: [PATCH 11/12] fix(usage.tsx): make separate call for top api keys --- litellm/proxy/proxy_server.py | 63 ++++++++++++- .../src/components/networking.tsx | 64 ++++++++++++- ui/litellm-dashboard/src/components/usage.tsx | 91 ++++++++++++------- 3 files changed, 178 insertions(+), 40 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 5e03b5f7017..069d40487be 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -4054,7 +4054,7 @@ async def view_spend_logs( ) async def global_spend_logs(): """ - [BETA] This is a beta endpoint. + [BETA] This is a beta endpoint. It will change. Use this to get global spend (spend per day for last 30d). Admin-only endpoint @@ -4069,6 +4069,61 @@ async def global_spend_logs(): return response +@router.get( + "/global/spend/keys", + tags=["Budget & Spend Tracking"], + dependencies=[Depends(user_api_key_auth)], +) +async def global_spend_keys( + limit: int = fastapi.Query( + default=None, + description="Number of keys to get. Will return Top 'n' keys.", + ) +): + """ + [BETA] This is a beta endpoint. It will change. + + Use this to get the top 'n' keys with the highest spend, ordered by spend. + """ + global prisma_client + + if prisma_client is None: + raise HTTPException(status_code=500, detail={"error": "No db connected"}) + sql_query = f"""SELECT * FROM "Last30dKeysBySpend" LIMIT {limit};""" + + response = await prisma_client.db.query_raw(query=sql_query) + + return response + + +@router.get( + "/global/spend/models", + tags=["Budget & Spend Tracking"], + dependencies=[Depends(user_api_key_auth)], +) +async def global_spend_models( + limit: int = fastapi.Query( + default=None, + description="Number of models to get. Will return Top 'n' models.", + ) +): + """ + [BETA] This is a beta endpoint. It will change. + + Use this to get the top 'n' keys with the highest spend, ordered by spend. + """ + global prisma_client + + if prisma_client is None: + raise HTTPException(status_code=500, detail={"error": "No db connected"}) + + sql_query = f"""SELECT * FROM "Last30dModelsBySpend" LIMIT {limit};""" + + response = await prisma_client.db.query_raw(query=sql_query) + + return response + + @router.get( "/daily_metrics", summary="Get daily spend metrics", @@ -4085,7 +4140,11 @@ async def view_daily_metrics( description="Time till which to view key spend", ), ): - """ """ + """ + [BETA] This is a beta endpoint. It might change without notice. + + Please give feedback - https://github.com/BerriAI/litellm/issues + """ try: if os.getenv("CLICKHOUSE_HOST") is not None: # gettting spend logs from clickhouse diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 1a6b47985c0..749fbb35e56 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -314,10 +314,6 @@ export const userSpendLogsCall = async ( ) => { try { console.log(`user role in spend logs call: ${userRole}`); - if (userRole == "Admin") { - return await adminSpendLogsCall(accessToken); - } - let url = proxyBaseUrl ? `${proxyBaseUrl}/spend/logs` : `/spend/logs`; if (userRole == "App Owner") { url = `${url}/?user_id=${userID}&start_date=${startTime}&end_date=${endTime}`; @@ -378,6 +374,66 @@ export const adminSpendLogsCall = async (accessToken: String) => { } }; +export const adminTopKeysCall = async (accessToken: String) => { + try { + let url = proxyBaseUrl + ? `${proxyBaseUrl}/global/spend/keys?limit=5` + : `/global/spend/keys?limit=5`; + + message.info("Making spend keys request"); + const response = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + if (!response.ok) { + const errorData = await response.text(); + message.error(errorData); + throw new Error("Network response was not ok"); + } + + const data = await response.json(); + console.log(data); + message.success("Spend Logs received"); + return data; + } catch (error) { + console.error("Failed to create key:", error); + throw error; + } +}; + +export const adminTopModelsCall = async (accessToken: String) => { + try { + let url = proxyBaseUrl + ? `${proxyBaseUrl}/global/spend/models?limit=5` + : `/global/spend/models?limit=5`; + + message.info("Making spend models request"); + const response = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + if (!response.ok) { + const errorData = await response.text(); + message.error(errorData); + throw new Error("Network response was not ok"); + } + + const data = await response.json(); + console.log(data); + message.success("Spend Logs received"); + return data; + } catch (error) { + console.error("Failed to create key:", error); + throw error; + } +}; + export const keyInfoCall = async (accessToken: String, keys: String[]) => { try { let url = proxyBaseUrl ? `${proxyBaseUrl}/v2/key/info` : `/v2/key/info`; diff --git a/ui/litellm-dashboard/src/components/usage.tsx b/ui/litellm-dashboard/src/components/usage.tsx index 1b60df3d475..afc002fd548 100644 --- a/ui/litellm-dashboard/src/components/usage.tsx +++ b/ui/litellm-dashboard/src/components/usage.tsx @@ -6,6 +6,8 @@ import { userSpendLogsCall, keyInfoCall, adminSpendLogsCall, + adminTopKeysCall, + adminTopModelsCall, } from "./networking"; import { start } from "repl"; @@ -168,40 +170,61 @@ const UsagePage: React.FC = ({ if (accessToken && token && userRole && userID) { const fetchData = async () => { try { - await userSpendLogsCall( - accessToken, - token, - userRole, - userID, - startTime, - endTime - ).then(async (response) => { - console.log("result from spend logs call", response); - if ("daily_spend" in response) { - // this is from clickhouse analytics - // - let daily_spend = response["daily_spend"]; - console.log("daily spend", daily_spend); - setKeySpendData(daily_spend); - let topApiKeys = response.top_api_keys; - setTopKeys(topApiKeys); - } else { - // const topKeysResponse = await keyInfoCall( - // accessToken, - // getTopKeys(response) - // ); - // const filtered_keys = topKeysResponse["info"].map((k: any) => ({ - // key: (k["key_name"] || k["key_alias"] || k["token"]).substring( - // 0, - // 7 - // ), - // spend: k["spend"], - // })); - // setTopKeys(filtered_keys); - // setTopUsers(getTopUsers(response)); - setKeySpendData(response); - } - }); + /** + * If user is Admin - query the global views endpoints + * If user is App Owner - use the normal spend logs call + */ + console.log(`user role: ${userRole}`); + if (userRole == "Admin") { + const overall_spend = await adminSpendLogsCall(accessToken); + setKeySpendData(overall_spend); + const top_keys = await adminTopKeysCall(accessToken); + const filtered_keys = top_keys.map((k: any) => ({ + key: (k["key_name"] || k["key_alias"] || k["api_key"]).substring( + 0, + 7 + ), + spend: k["total_spend"], + })); + setTopKeys(filtered_keys); + const top_models = await adminTopModelsCall(accessToken); + } else if (userRole == "App Owner") { + await userSpendLogsCall( + accessToken, + token, + userRole, + userID, + startTime, + endTime + ).then(async (response) => { + console.log("result from spend logs call", response); + if ("daily_spend" in response) { + // this is from clickhouse analytics + // + let daily_spend = response["daily_spend"]; + console.log("daily spend", daily_spend); + setKeySpendData(daily_spend); + let topApiKeys = response.top_api_keys; + setTopKeys(topApiKeys); + } else { + const topKeysResponse = await keyInfoCall( + accessToken, + getTopKeys(response) + ); + const filtered_keys = topKeysResponse["info"].map((k: any) => ({ + key: ( + k["key_name"] || + k["key_alias"] || + k["token"] + ).substring(0, 7), + spend: k["spend"], + })); + setTopKeys(filtered_keys); + setTopUsers(getTopUsers(response)); + setKeySpendData(response); + } + }); + } } catch (error) { console.error("There was an error fetching the data", error); // Optionally, update your UI to reflect the error state here as well From 3697ef45806d4619888a0d2ca049ad0a7865874d Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 28 Feb 2024 16:56:55 -0800 Subject: [PATCH 12/12] test: skip aws test - aws account suspended --- litellm/tests/test_completion.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index f502e5f0388..a3b7f579832 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -1553,6 +1553,7 @@ def test_completion_bedrock_titan(): # test_completion_bedrock_titan() +@pytest.mark.skip(reason="AWS Suspended Account") def test_completion_bedrock_claude(): print("calling claude") try: