mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-16 23:41:43 +00:00
Merge branch 'main' into litellm_invite_users_via_link
This commit is contained in:
commit
bcbc250a12
10 changed files with 594 additions and 234 deletions
|
|
@ -2,12 +2,6 @@ import Image from '@theme/IdealImage';
|
|||
|
||||
# ✨ 📧 Email Notifications
|
||||
|
||||
:::info
|
||||
|
||||
This is an Enterprise only feature [Get in touch with us for a Free Trial](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat)
|
||||
|
||||
:::
|
||||
|
||||
Send an Email to your users when:
|
||||
- A Proxy API Key is created for them
|
||||
- Their API Key crosses it's Budget
|
||||
|
|
@ -38,6 +32,12 @@ That's it ! start your proxy
|
|||
|
||||
## Customizing Email Branding
|
||||
|
||||
:::info
|
||||
|
||||
Customizing Email Branding is an Enterprise Feature [Get in touch with us for a Free Trial](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat)
|
||||
|
||||
:::
|
||||
|
||||
LiteLLM allows you to customize the:
|
||||
- Logo on the Email
|
||||
- Email support contact
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Requirements:
|
|||
You can set budgets at 3 levels:
|
||||
- For the proxy
|
||||
- For an internal user
|
||||
- For an end-user
|
||||
- For a customer (end-user)
|
||||
- For a key
|
||||
- For a key (model specific budgets)
|
||||
|
||||
|
|
@ -173,7 +173,7 @@ curl --location 'http://localhost:4000/chat/completions' \
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="per-user-chat" label="For End User">
|
||||
<TabItem value="per-user-chat" label="For Customers">
|
||||
|
||||
Use this to budget `user` passed to `/chat/completions`, **without needing to create a key for every user**
|
||||
|
||||
|
|
@ -452,7 +452,7 @@ curl --location 'http://0.0.0.0:4000/key/generate' \
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="per-end-user" label="For End User">
|
||||
<TabItem value="per-end-user" label="For customers">
|
||||
|
||||
:::info
|
||||
|
||||
|
|
@ -477,12 +477,12 @@ curl --location 'http://0.0.0.0:4000/budget/new' \
|
|||
```
|
||||
|
||||
|
||||
#### Step 2. Create `End-User` with Budget
|
||||
#### Step 2. Create `Customer` with Budget
|
||||
|
||||
We use `budget_id="free-tier"` from Step 1 when creating this new end user
|
||||
We use `budget_id="free-tier"` from Step 1 when creating this new customers
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/end_user/new' \
|
||||
curl --location 'http://0.0.0.0:4000/customer/new' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
|
|
@ -492,7 +492,7 @@ curl --location 'http://0.0.0.0:4000/end_user/new' \
|
|||
```
|
||||
|
||||
|
||||
#### Step 3. Pass end user id in `/chat/completions` requests
|
||||
#### Step 3. Pass `user_id` id in `/chat/completions` requests
|
||||
|
||||
Pass the `user_id` from Step 2 as `user="palantir"`
|
||||
|
||||
|
|
|
|||
|
|
@ -41,10 +41,6 @@ class ProviderRegionOutageModel(BaseOutageModel):
|
|||
|
||||
# we use this for the email header, please send a test email if you change this. verify it looks good on email
|
||||
LITELLM_LOGO_URL = "https://litellm-listing.s3.amazonaws.com/litellm_logo.png"
|
||||
EMAIL_LOGO_URL = os.getenv(
|
||||
"SMTP_SENDER_LOGO", "https://litellm-listing.s3.amazonaws.com/litellm_logo.png"
|
||||
)
|
||||
EMAIL_SUPPORT_CONTACT = os.getenv("EMAIL_SUPPORT_CONTACT", "support@berri.ai")
|
||||
|
||||
|
||||
class LiteLLMBase(BaseModel):
|
||||
|
|
@ -1147,21 +1143,34 @@ Model Info:
|
|||
|
||||
return False
|
||||
|
||||
async def _check_if_using_premium_email_feature(
|
||||
self,
|
||||
premium_user: bool,
|
||||
email_logo_url: Optional[str] = None,
|
||||
email_support_contact: Optional[str] = None,
|
||||
):
|
||||
from litellm.proxy.proxy_server import premium_user
|
||||
from litellm.proxy.proxy_server import CommonProxyErrors
|
||||
|
||||
if premium_user is not True:
|
||||
if email_logo_url is not None or email_support_contact is not None:
|
||||
raise ValueError(
|
||||
f"Trying to Customize Email Alerting\n {CommonProxyErrors.not_premium_user.value}"
|
||||
)
|
||||
|
||||
async def send_key_created_email(self, webhook_event: WebhookEvent) -> bool:
|
||||
from litellm.proxy.utils import send_email
|
||||
|
||||
if self.alerting is None or "email" not in self.alerting:
|
||||
# do nothing if user does not want email alerts
|
||||
return False
|
||||
from litellm.proxy.proxy_server import premium_user, prisma_client
|
||||
|
||||
# make sure this is a premium user
|
||||
from litellm.proxy.proxy_server import premium_user
|
||||
from litellm.proxy.proxy_server import CommonProxyErrors, prisma_client
|
||||
|
||||
if premium_user != True:
|
||||
raise Exception(
|
||||
f"Trying to use Email Alerting on key creation\n {CommonProxyErrors.not_premium_user.value}"
|
||||
)
|
||||
email_logo_url = os.getenv("SMTP_SENDER_LOGO", None)
|
||||
email_support_contact = os.getenv("EMAIL_SUPPORT_CONTACT", None)
|
||||
await self._check_if_using_premium_email_feature(
|
||||
premium_user, email_logo_url, email_support_contact
|
||||
)
|
||||
|
||||
event_name = webhook_event.event_message
|
||||
recipient_email = webhook_event.user_email
|
||||
|
|
@ -1188,7 +1197,7 @@ Model Info:
|
|||
"Trying to send email alert to no recipient", extra=webhook_event.dict()
|
||||
)
|
||||
email_html_content = f"""
|
||||
<img src="{EMAIL_LOGO_URL}" alt="LiteLLM Logo" width="150" height="50" />
|
||||
<img src="{email_logo_url}" alt="LiteLLM Logo" width="150" height="50" />
|
||||
|
||||
<p> Hi {recipient_email}, <br/>
|
||||
|
||||
|
|
@ -1223,7 +1232,7 @@ Model Info:
|
|||
</pre>
|
||||
|
||||
|
||||
If you have any questions, please send an email to {EMAIL_SUPPORT_CONTACT} <br /> <br />
|
||||
If you have any questions, please send an email to {email_support_contact} <br /> <br />
|
||||
|
||||
Best, <br />
|
||||
The LiteLLM team <br />
|
||||
|
|
@ -1254,6 +1263,14 @@ Model Info:
|
|||
"""
|
||||
from litellm.proxy.utils import send_email
|
||||
|
||||
from litellm.proxy.proxy_server import premium_user, prisma_client
|
||||
|
||||
email_logo_url = os.getenv("SMTP_SENDER_LOGO", None)
|
||||
email_support_contact = os.getenv("EMAIL_SUPPORT_CONTACT", None)
|
||||
await self._check_if_using_premium_email_feature(
|
||||
premium_user, email_logo_url, email_support_contact
|
||||
)
|
||||
|
||||
event_name = webhook_event.event_message
|
||||
recipient_email = webhook_event.user_email
|
||||
user_name = webhook_event.user_id
|
||||
|
|
@ -1266,7 +1283,7 @@ Model Info:
|
|||
|
||||
if webhook_event.event == "budget_crossed":
|
||||
email_html_content = f"""
|
||||
<img src="{EMAIL_LOGO_URL}" alt="LiteLLM Logo" width="150" height="50" />
|
||||
<img src="{email_logo_url}" alt="LiteLLM Logo" width="150" height="50" />
|
||||
|
||||
<p> Hi {user_name}, <br/>
|
||||
|
||||
|
|
@ -1274,7 +1291,7 @@ Model Info:
|
|||
|
||||
API requests will be rejected until either (a) you increase your monthly budget or (b) your monthly usage resets at the beginning of the next calendar month. <br /> <br />
|
||||
|
||||
If you have any questions, please send an email to {EMAIL_SUPPORT_CONTACT} <br /> <br />
|
||||
If you have any questions, please send an email to {email_support_contact} <br /> <br />
|
||||
|
||||
Best, <br />
|
||||
The LiteLLM team <br />
|
||||
|
|
|
|||
|
|
@ -519,7 +519,11 @@ class UpdateUserRequest(GenerateRequestBase):
|
|||
return values
|
||||
|
||||
|
||||
class NewEndUserRequest(LiteLLMBase):
|
||||
class NewCustomerRequest(LiteLLMBase):
|
||||
"""
|
||||
Create a new customer, allocate a budget to them
|
||||
"""
|
||||
|
||||
user_id: str
|
||||
alias: Optional[str] = None # human-friendly alias
|
||||
blocked: bool = False # allow/disallow requests for this end-user
|
||||
|
|
@ -540,6 +544,33 @@ class NewEndUserRequest(LiteLLMBase):
|
|||
return values
|
||||
|
||||
|
||||
class UpdateCustomerRequest(LiteLLMBase):
|
||||
"""
|
||||
Update a Customer, use this to update customer budgets etc
|
||||
|
||||
"""
|
||||
|
||||
user_id: str
|
||||
alias: Optional[str] = None # human-friendly alias
|
||||
blocked: bool = False # allow/disallow requests for this end-user
|
||||
max_budget: Optional[float] = None
|
||||
budget_id: Optional[str] = None # give either a budget_id or max_budget
|
||||
allowed_model_region: Optional[Literal["eu"]] = (
|
||||
None # require all user requests to use models in this specific region
|
||||
)
|
||||
default_model: Optional[str] = (
|
||||
None # if no equivalent model in allowed region - default all requests to this model
|
||||
)
|
||||
|
||||
|
||||
class DeleteCustomerRequest(LiteLLMBase):
|
||||
"""
|
||||
Delete multiple Customers
|
||||
"""
|
||||
|
||||
user_ids: List[str]
|
||||
|
||||
|
||||
class Member(LiteLLMBase):
|
||||
role: Literal["admin", "user"]
|
||||
user_id: Optional[str] = None
|
||||
|
|
@ -928,6 +959,10 @@ class ConfigGeneralSettings(LiteLLMBase):
|
|||
allowed_routes: Optional[List] = Field(
|
||||
None, description="Proxy API Endpoints you want users to be able to access"
|
||||
)
|
||||
enable_public_model_hub: bool = Field(
|
||||
default=False,
|
||||
description="Public model hub for users to see what models they have access to, supported openai params, etc.",
|
||||
)
|
||||
|
||||
|
||||
class ConfigYAML(LiteLLMBase):
|
||||
|
|
@ -1179,3 +1214,7 @@ class InvitationModel(LiteLLMBase):
|
|||
created_by: str
|
||||
updated_at: datetime
|
||||
updated_by: str
|
||||
|
||||
class ConfigFieldInfo(LiteLLMBase):
|
||||
field_name: str
|
||||
field_value: Any
|
||||
|
|
|
|||
|
|
@ -7137,13 +7137,15 @@ async def global_predict_spend_logs(request: Request):
|
|||
#### INTERNAL USER MANAGEMENT ####
|
||||
@router.post(
|
||||
"/user/new",
|
||||
tags=["user management"],
|
||||
tags=["Internal User management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
response_model=NewUserResponse,
|
||||
)
|
||||
async def new_user(data: NewUserRequest):
|
||||
"""
|
||||
Use this to create a new user with a budget. This creates a new user and generates a new api key for the new user. The new api key is returned.
|
||||
Use this to create a new INTERNAL user with a budget.
|
||||
Internal Users can access LiteLLM Admin UI to make keys, request access to models.
|
||||
This creates a new user and generates a new api key for the new user. The new api key is returned.
|
||||
|
||||
Returns user id, budget + new key.
|
||||
|
||||
|
|
@ -7214,7 +7216,9 @@ async def new_user(data: NewUserRequest):
|
|||
|
||||
|
||||
@router.post(
|
||||
"/user/auth", tags=["user management"], dependencies=[Depends(user_api_key_auth)]
|
||||
"/user/auth",
|
||||
tags=["Internal User management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def user_auth(request: Request):
|
||||
"""
|
||||
|
|
@ -7280,7 +7284,9 @@ async def user_auth(request: Request):
|
|||
|
||||
|
||||
@router.get(
|
||||
"/user/info", tags=["user management"], dependencies=[Depends(user_api_key_auth)]
|
||||
"/user/info",
|
||||
tags=["Internal User management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def user_info(
|
||||
user_id: Optional[str] = fastapi.Query(
|
||||
|
|
@ -7452,7 +7458,9 @@ async def user_info(
|
|||
|
||||
|
||||
@router.post(
|
||||
"/user/update", tags=["user management"], dependencies=[Depends(user_api_key_auth)]
|
||||
"/user/update",
|
||||
tags=["Internal User management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def user_update(data: UpdateUserRequest):
|
||||
"""
|
||||
|
|
@ -7546,7 +7554,7 @@ async def user_update(data: UpdateUserRequest):
|
|||
|
||||
@router.post(
|
||||
"/user/request_model",
|
||||
tags=["user management"],
|
||||
tags=["Internal User management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def user_request_model(request: Request):
|
||||
|
|
@ -7599,7 +7607,7 @@ async def user_request_model(request: Request):
|
|||
|
||||
@router.get(
|
||||
"/user/get_requests",
|
||||
tags=["user management"],
|
||||
tags=["Internal User management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def user_get_requests():
|
||||
|
|
@ -7641,7 +7649,7 @@ async def user_get_requests():
|
|||
|
||||
@router.get(
|
||||
"/user/get_users",
|
||||
tags=["user management"],
|
||||
tags=["Internal User management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def get_users(
|
||||
|
|
@ -7678,7 +7686,13 @@ async def get_users(
|
|||
|
||||
@router.post(
|
||||
"/end_user/block",
|
||||
tags=["End User Management"],
|
||||
tags=["Customer Management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
include_in_schema=False,
|
||||
)
|
||||
@router.post(
|
||||
"/customer/block",
|
||||
tags=["Customer Management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def block_user(data: BlockUsers):
|
||||
|
|
@ -7721,9 +7735,15 @@ async def block_user(data: BlockUsers):
|
|||
|
||||
@router.post(
|
||||
"/end_user/unblock",
|
||||
tags=["End User Management"],
|
||||
tags=["Customer Management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
@router.post(
|
||||
"/customer/unblock",
|
||||
tags=["Customer Management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
include_in_schema=False,
|
||||
)
|
||||
async def unblock_user(data: BlockUsers):
|
||||
"""
|
||||
[BETA] Unblock calls with this user id
|
||||
|
|
@ -7768,35 +7788,36 @@ async def unblock_user(data: BlockUsers):
|
|||
|
||||
@router.post(
|
||||
"/end_user/new",
|
||||
tags=["End User Management"],
|
||||
tags=["Customer Management"],
|
||||
include_in_schema=False,
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
@router.post(
|
||||
"/customer/new",
|
||||
tags=["Customer Management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def new_end_user(
|
||||
data: NewEndUserRequest,
|
||||
data: NewCustomerRequest,
|
||||
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
|
||||
):
|
||||
"""
|
||||
[TODO] Needs to be implemented.
|
||||
|
||||
Allow creating a new end-user
|
||||
Allow creating a new Customer
|
||||
NOTE: This used to be called `/end_user/new`, we will still be maintaining compatibility for /end_user/XXX for these endpoints
|
||||
|
||||
- Allow specifying allowed regions
|
||||
- Allow specifying default model
|
||||
|
||||
Example curl:
|
||||
```
|
||||
curl --location 'http://0.0.0.0:4000/end_user/new' \
|
||||
curl --location 'http://0.0.0.0:4000/customer/new' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"end_user_id" : "ishaan-jaff-3", <- specific customer
|
||||
|
||||
"allowed_region": "eu" <- set region for models
|
||||
|
||||
+
|
||||
|
||||
"user_id" : "ishaan-jaff-3",
|
||||
"allowed_region": "eu",
|
||||
"budget_id": "free_tier",
|
||||
"default_model": "azure/gpt-3.5-turbo-eu" <- all calls from this user, use this model?
|
||||
|
||||
}'
|
||||
|
||||
# return end-user object
|
||||
|
|
@ -7819,56 +7840,88 @@ async def new_end_user(
|
|||
status_code=500,
|
||||
detail={"error": CommonProxyErrors.db_not_connected_error.value},
|
||||
)
|
||||
try:
|
||||
|
||||
## VALIDATION ##
|
||||
if data.default_model is not None:
|
||||
if llm_router is None:
|
||||
raise HTTPException(
|
||||
status_code=422, detail={"error": CommonProxyErrors.no_llm_router.value}
|
||||
)
|
||||
elif data.default_model not in llm_router.get_model_names():
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail={
|
||||
"error": "Default Model not on proxy. Configure via `/model/new` or config.yaml. Default_model={}, proxy_model_names={}".format(
|
||||
data.default_model, set(llm_router.get_model_names())
|
||||
)
|
||||
},
|
||||
## VALIDATION ##
|
||||
if data.default_model is not None:
|
||||
if llm_router is None:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail={"error": CommonProxyErrors.no_llm_router.value},
|
||||
)
|
||||
elif data.default_model not in llm_router.get_model_names():
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail={
|
||||
"error": "Default Model not on proxy. Configure via `/model/new` or config.yaml. Default_model={}, proxy_model_names={}".format(
|
||||
data.default_model, set(llm_router.get_model_names())
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
new_end_user_obj: Dict = {}
|
||||
|
||||
## CREATE BUDGET ## if set
|
||||
if data.max_budget is not None:
|
||||
budget_record = await prisma_client.db.litellm_budgettable.create(
|
||||
data={
|
||||
"max_budget": data.max_budget,
|
||||
"created_by": user_api_key_dict.user_id or litellm_proxy_admin_name, # type: ignore
|
||||
"updated_by": user_api_key_dict.user_id or litellm_proxy_admin_name,
|
||||
}
|
||||
)
|
||||
|
||||
new_end_user_obj: Dict = {}
|
||||
new_end_user_obj["budget_id"] = budget_record.budget_id
|
||||
elif data.budget_id is not None:
|
||||
new_end_user_obj["budget_id"] = data.budget_id
|
||||
|
||||
## CREATE BUDGET ## if set
|
||||
if data.max_budget is not None:
|
||||
budget_record = await prisma_client.db.litellm_budgettable.create(
|
||||
data={
|
||||
"max_budget": data.max_budget,
|
||||
"created_by": user_api_key_dict.user_id or litellm_proxy_admin_name, # type: ignore
|
||||
"updated_by": user_api_key_dict.user_id or litellm_proxy_admin_name,
|
||||
}
|
||||
_user_data = data.dict(exclude_none=True)
|
||||
|
||||
for k, v in _user_data.items():
|
||||
if k != "max_budget" and k != "budget_id":
|
||||
new_end_user_obj[k] = v
|
||||
|
||||
## WRITE TO DB ##
|
||||
end_user_record = await prisma_client.db.litellm_endusertable.create(
|
||||
data=new_end_user_obj # type: ignore
|
||||
)
|
||||
|
||||
new_end_user_obj["budget_id"] = budget_record.budget_id
|
||||
elif data.budget_id is not None:
|
||||
new_end_user_obj["budget_id"] = data.budget_id
|
||||
return end_user_record
|
||||
except Exception as e:
|
||||
if "Unique constraint failed on the fields: (`user_id`)" in str(e):
|
||||
raise ProxyException(
|
||||
message=f"Customer already exists, passed user_id={data.user_id}. Please pass a new user_id.",
|
||||
type="bad_request",
|
||||
code=400,
|
||||
param="user_id",
|
||||
)
|
||||
|
||||
_user_data = data.dict(exclude_none=True)
|
||||
|
||||
for k, v in _user_data.items():
|
||||
if k != "max_budget" and k != "budget_id":
|
||||
new_end_user_obj[k] = v
|
||||
|
||||
## WRITE TO DB ##
|
||||
end_user_record = await prisma_client.db.litellm_endusertable.create(
|
||||
data=new_end_user_obj # type: ignore
|
||||
)
|
||||
|
||||
return end_user_record
|
||||
if isinstance(e, HTTPException):
|
||||
raise ProxyException(
|
||||
message=getattr(e, "detail", f"Internal Server Error({str(e)})"),
|
||||
type="internal_error",
|
||||
param=getattr(e, "param", "None"),
|
||||
code=getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR),
|
||||
)
|
||||
elif isinstance(e, ProxyException):
|
||||
raise e
|
||||
raise ProxyException(
|
||||
message="Internal Server Error, " + str(e),
|
||||
type="internal_error",
|
||||
param=getattr(e, "param", "None"),
|
||||
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/customer/info",
|
||||
tags=["Customer Management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
@router.get(
|
||||
"/end_user/info",
|
||||
tags=["End User Management"],
|
||||
tags=["Customer Management"],
|
||||
include_in_schema=False,
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def end_user_info(
|
||||
|
|
@ -7892,26 +7945,174 @@ async def end_user_info(
|
|||
|
||||
|
||||
@router.post(
|
||||
"/end_user/update",
|
||||
tags=["End User Management"],
|
||||
"/customer/update",
|
||||
tags=["Customer Management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def update_end_user():
|
||||
@router.post(
|
||||
"/end_user/update",
|
||||
tags=["Customer Management"],
|
||||
include_in_schema=False,
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def update_end_user(
|
||||
data: UpdateCustomerRequest,
|
||||
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
|
||||
):
|
||||
"""
|
||||
[TODO] Needs to be implemented.
|
||||
Example curl
|
||||
|
||||
```
|
||||
curl --location 'http://0.0.0.0:4000/customer/update' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"user_id": "test-litellm-user-4",
|
||||
"budget_id": "paid_tier"
|
||||
}'
|
||||
|
||||
See below for all params
|
||||
```
|
||||
"""
|
||||
|
||||
global prisma_client
|
||||
try:
|
||||
data_json: dict = data.json()
|
||||
# get the row from db
|
||||
if prisma_client is None:
|
||||
raise Exception("Not connected to DB!")
|
||||
|
||||
# get non default values for key
|
||||
non_default_values = {}
|
||||
for k, v in data_json.items():
|
||||
if v is not None and v not in (
|
||||
[],
|
||||
{},
|
||||
0,
|
||||
): # models default to [], spend defaults to 0, we should not reset these values
|
||||
non_default_values[k] = v
|
||||
|
||||
## ADD USER, IF NEW ##
|
||||
verbose_proxy_logger.debug("/customer/update: Received data = %s", data)
|
||||
if data.user_id is not None and len(data.user_id) > 0:
|
||||
non_default_values["user_id"] = data.user_id # type: ignore
|
||||
verbose_proxy_logger.debug("In update customer, user_id condition block.")
|
||||
response = await prisma_client.db.litellm_endusertable.update(
|
||||
where={"user_id": data.user_id}, data=non_default_values # type: ignore
|
||||
)
|
||||
if response is None:
|
||||
raise ValueError(
|
||||
f"Failed updating customer data. User ID does not exist passed user_id={data.user_id}"
|
||||
)
|
||||
verbose_proxy_logger.debug(
|
||||
f"received response from updating prisma client. response={response}"
|
||||
)
|
||||
return response
|
||||
else:
|
||||
raise ValueError(f"user_id is required, passed user_id = {data.user_id}")
|
||||
|
||||
# update based on remaining passed in values
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
if isinstance(e, HTTPException):
|
||||
raise ProxyException(
|
||||
message=getattr(e, "detail", f"Internal Server Error({str(e)})"),
|
||||
type="internal_error",
|
||||
param=getattr(e, "param", "None"),
|
||||
code=getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR),
|
||||
)
|
||||
elif isinstance(e, ProxyException):
|
||||
raise e
|
||||
raise ProxyException(
|
||||
message="Internal Server Error, " + str(e),
|
||||
type="internal_error",
|
||||
param=getattr(e, "param", "None"),
|
||||
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
pass
|
||||
|
||||
|
||||
@router.post(
|
||||
"/end_user/delete",
|
||||
tags=["End User Management"],
|
||||
"/customer/delete",
|
||||
tags=["Customer Management"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def delete_end_user():
|
||||
@router.post(
|
||||
"/end_user/delete",
|
||||
tags=["Customer Management"],
|
||||
include_in_schema=False,
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def delete_end_user(
|
||||
data: DeleteCustomerRequest,
|
||||
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
|
||||
):
|
||||
"""
|
||||
[TODO] Needs to be implemented.
|
||||
Example curl
|
||||
|
||||
```
|
||||
curl --location 'http://0.0.0.0:4000/customer/delete' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"user_ids" :["ishaan-jaff-5"]
|
||||
}'
|
||||
|
||||
See below for all params
|
||||
```
|
||||
"""
|
||||
global prisma_client
|
||||
|
||||
try:
|
||||
if prisma_client is None:
|
||||
raise Exception("Not connected to DB!")
|
||||
|
||||
verbose_proxy_logger.debug("/customer/delete: Received data = %s", data)
|
||||
if (
|
||||
data.user_ids is not None
|
||||
and isinstance(data.user_ids, list)
|
||||
and len(data.user_ids) > 0
|
||||
):
|
||||
response = await prisma_client.db.litellm_endusertable.delete_many(
|
||||
where={"user_id": {"in": data.user_ids}}
|
||||
)
|
||||
if response is None:
|
||||
raise ValueError(
|
||||
f"Failed deleting customer data. User ID does not exist passed user_id={data.user_ids}"
|
||||
)
|
||||
if response != len(data.user_ids):
|
||||
raise ValueError(
|
||||
f"Failed deleting all customer data. User ID does not exist passed user_id={data.user_ids}. Deleted {response} customers, passed {len(data.user_ids)} customers"
|
||||
)
|
||||
verbose_proxy_logger.debug(
|
||||
f"received response from updating prisma client. response={response}"
|
||||
)
|
||||
return {
|
||||
"deleted_customers": response,
|
||||
"message": "Successfully deleted customers with ids: "
|
||||
+ str(data.user_ids),
|
||||
}
|
||||
else:
|
||||
raise ValueError(f"user_id is required, passed user_id = {data.user_ids}")
|
||||
|
||||
# update based on remaining passed in values
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
if isinstance(e, HTTPException):
|
||||
raise ProxyException(
|
||||
message=getattr(e, "detail", f"Internal Server Error({str(e)})"),
|
||||
type="internal_error",
|
||||
param=getattr(e, "param", "None"),
|
||||
code=getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR),
|
||||
)
|
||||
elif isinstance(e, ProxyException):
|
||||
raise e
|
||||
raise ProxyException(
|
||||
message="Internal Server Error, " + str(e),
|
||||
type="internal_error",
|
||||
param=getattr(e, "param", "None"),
|
||||
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -11297,6 +11498,7 @@ async def update_config_general_settings(
|
|||
"/config/field/info",
|
||||
tags=["config.yaml"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
response_model=ConfigFieldInfo,
|
||||
)
|
||||
async def get_config_general_settings(
|
||||
field_name: str,
|
||||
|
|
@ -11343,10 +11545,9 @@ async def get_config_general_settings(
|
|||
general_settings = dict(db_general_settings.param_value)
|
||||
|
||||
if field_name in general_settings:
|
||||
return {
|
||||
"field_name": field_name,
|
||||
"field_value": general_settings[field_name],
|
||||
}
|
||||
return ConfigFieldInfo(
|
||||
field_name=field_name, field_value=general_settings[field_name]
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
|
|
|
|||
|
|
@ -1850,20 +1850,13 @@ async def send_email(receiver_email, subject, html):
|
|||
from litellm.proxy.proxy_server import premium_user
|
||||
from litellm.proxy.proxy_server import CommonProxyErrors
|
||||
|
||||
# Check if user is premium - This is an Enterprise only Feature
|
||||
if premium_user != True:
|
||||
raise Exception(
|
||||
f"Trying to use Email Alerting\n {CommonProxyErrors.not_premium_user.value}"
|
||||
)
|
||||
# Done Checking
|
||||
|
||||
smtp_host = os.getenv("SMTP_HOST")
|
||||
smtp_port = os.getenv("SMTP_PORT", 587) # default to port 587
|
||||
smtp_port = int(os.getenv("SMTP_PORT", "587")) # default to port 587
|
||||
smtp_username = os.getenv("SMTP_USERNAME")
|
||||
smtp_password = os.getenv("SMTP_PASSWORD")
|
||||
sender_email = os.getenv("SMTP_SENDER_EMAIL", None)
|
||||
if sender_email is None:
|
||||
raise Exception("Trying to use SMTP, but SMTP_SENDER_EMAIL is not set")
|
||||
raise ValueError("Trying to use SMTP, but SMTP_SENDER_EMAIL is not set")
|
||||
|
||||
## EMAIL SETUP ##
|
||||
email_message = MIMEMultipart()
|
||||
|
|
|
|||
25
ui/litellm-dashboard/src/app/model_hub/page.tsx
Normal file
25
ui/litellm-dashboard/src/app/model_hub/page.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
"use client";
|
||||
import React, { Suspense, useEffect, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { modelHubCall } from "@/components/networking";
|
||||
import ModelHub from "@/components/model_hub";
|
||||
|
||||
export default function PublicModelHub() {
|
||||
const searchParams = useSearchParams();
|
||||
const key = searchParams.get("key");
|
||||
const [accessToken, setAccessToken] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
setAccessToken(key);
|
||||
}, [key]);
|
||||
/**
|
||||
* populate navbar
|
||||
*
|
||||
*/
|
||||
return (
|
||||
<ModelHub accessToken={accessToken} publicPage={true} premiumUser={false} />
|
||||
);
|
||||
}
|
||||
|
|
@ -18,6 +18,30 @@ import Usage from "../components/usage";
|
|||
import { jwtDecode } from "jwt-decode";
|
||||
import { Typography } from "antd";
|
||||
|
||||
export function formatUserRole(userRole: string) {
|
||||
if (!userRole) {
|
||||
return "Undefined Role";
|
||||
}
|
||||
console.log(`Received user role: ${userRole.toLowerCase()}`);
|
||||
console.log(`Received user role length: ${userRole.toLowerCase().length}`);
|
||||
switch (userRole.toLowerCase()) {
|
||||
case "app_owner":
|
||||
return "App Owner";
|
||||
case "demo_app_owner":
|
||||
return "App Owner";
|
||||
case "app_admin":
|
||||
return "Admin";
|
||||
case "proxy_admin":
|
||||
return "Admin";
|
||||
case "proxy_admin_viewer":
|
||||
return "Admin Viewer";
|
||||
case "app_user":
|
||||
return "App User";
|
||||
default:
|
||||
return "Unknown Role";
|
||||
}
|
||||
}
|
||||
|
||||
const CreateKeyPage = () => {
|
||||
const { Title, Paragraph } = Typography;
|
||||
const [userRole, setUserRole] = useState("");
|
||||
|
|
@ -78,30 +102,6 @@ const CreateKeyPage = () => {
|
|||
}
|
||||
}, [token]);
|
||||
|
||||
function formatUserRole(userRole: string) {
|
||||
if (!userRole) {
|
||||
return "Undefined Role";
|
||||
}
|
||||
console.log(`Received user role: ${userRole.toLowerCase()}`);
|
||||
console.log(`Received user role length: ${userRole.toLowerCase().length}`);
|
||||
switch (userRole.toLowerCase()) {
|
||||
case "app_owner":
|
||||
return "App Owner";
|
||||
case "demo_app_owner":
|
||||
return "App Owner";
|
||||
case "app_admin":
|
||||
return "Admin";
|
||||
case "proxy_admin":
|
||||
return "Admin";
|
||||
case "proxy_admin_viewer":
|
||||
return "Admin Viewer";
|
||||
case "app_user":
|
||||
return "App User";
|
||||
default:
|
||||
return "Unknown Role";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
|
|
@ -194,17 +194,13 @@ const CreateKeyPage = () => {
|
|||
accessToken={accessToken}
|
||||
modelData={modelData}
|
||||
/>
|
||||
) : page == "model-hub" ? (
|
||||
<ModelHub
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
) : page == "model-hub" ? (
|
||||
<ModelHub
|
||||
accessToken={accessToken}
|
||||
keys={keys}
|
||||
publicPage={false}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
) : (
|
||||
<Usage
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { useRouter, usePathname, useSearchParams } from "next/navigation";
|
||||
|
||||
import { modelHubCall } from "./networking";
|
||||
|
||||
import { getConfigFieldSetting, updateConfigFieldSetting } from "./networking";
|
||||
import {
|
||||
Card,
|
||||
Text,
|
||||
|
|
@ -15,18 +16,14 @@ import {
|
|||
TabPanel,
|
||||
TabPanels,
|
||||
} from "@tremor/react";
|
||||
|
||||
import { RightOutlined, CopyOutlined } from "@ant-design/icons";
|
||||
|
||||
import { Modal, Tooltip } from "antd";
|
||||
import { Modal, Tooltip, message } from "antd";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
|
||||
interface ModelHubProps {
|
||||
userID: string | null;
|
||||
userRole: string | null;
|
||||
token: string | null;
|
||||
accessToken: string | null;
|
||||
keys: any; // Replace with the appropriate type for 'keys' prop
|
||||
publicPage: boolean;
|
||||
premiumUser: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -38,46 +35,51 @@ interface ModelInfo {
|
|||
max_input_tokens?: number;
|
||||
max_output_tokens?: number;
|
||||
supported_openai_params?: string[];
|
||||
|
||||
// Add other properties if needed
|
||||
}
|
||||
|
||||
const ModelHub: React.FC<ModelHubProps> = ({
|
||||
userID,
|
||||
|
||||
userRole,
|
||||
|
||||
token,
|
||||
|
||||
accessToken,
|
||||
|
||||
keys,
|
||||
|
||||
publicPage,
|
||||
premiumUser,
|
||||
}) => {
|
||||
const [publicPageAllowed, setPublicPageAllowed] = useState<boolean>(false);
|
||||
const [modelHubData, setModelHubData] = useState<ModelInfo[] | null>(null);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [isPublicPageModalVisible, setIsPublicPageModalVisible] =
|
||||
useState(false);
|
||||
const [selectedModel, setSelectedModel] = useState<null | ModelInfo>(null);
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (!accessToken || !token || !userRole || !userID) {
|
||||
if (!accessToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const _modelHubData = await modelHubCall(accessToken, userID, userRole);
|
||||
const _modelHubData = await modelHubCall(accessToken);
|
||||
|
||||
console.log("ModelHubData:", _modelHubData);
|
||||
|
||||
setModelHubData(_modelHubData.data);
|
||||
|
||||
getConfigFieldSetting(accessToken, "enable_public_model_hub")
|
||||
.then((data) => {
|
||||
console.log(`data: ${JSON.stringify(data)}`);
|
||||
if (data.field_value == true) {
|
||||
setPublicPageAllowed(true);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// do nothing
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("There was an error fetching the model data", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [accessToken, token, userRole, userID]);
|
||||
}, [accessToken, publicPage]);
|
||||
|
||||
const showModal = (model: ModelInfo) => {
|
||||
setSelectedModel(model);
|
||||
|
|
@ -85,15 +87,29 @@ const ModelHub: React.FC<ModelHubProps> = ({
|
|||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const goToPublicModelPage = () => {
|
||||
router.replace(`/model_hub?key=${accessToken}`);
|
||||
};
|
||||
const handleMakePublicPage = async () => {
|
||||
if (!accessToken) {
|
||||
return;
|
||||
}
|
||||
updateConfigFieldSetting(accessToken, "enable_public_model_hub", true).then(
|
||||
(data) => {
|
||||
setIsPublicPageModalVisible(true);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
setIsModalVisible(false);
|
||||
|
||||
setIsPublicPageModalVisible(false);
|
||||
setSelectedModel(null);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setIsModalVisible(false);
|
||||
|
||||
setIsPublicPageModalVisible(false);
|
||||
setSelectedModel(null);
|
||||
};
|
||||
|
||||
|
|
@ -103,66 +119,112 @@ const ModelHub: React.FC<ModelHubProps> = ({
|
|||
|
||||
return (
|
||||
<div>
|
||||
<div className="w-full m-2 mt-2 p-8">
|
||||
<div className="relative w-full"></div>
|
||||
{(publicPage && publicPageAllowed) || publicPage == false ? (
|
||||
<div className="w-full m-2 mt-2 p-8">
|
||||
<div className="relative w-full"></div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<Title className="ml-8 text-center ">Model Hub</Title>
|
||||
<Button className="ml-4">
|
||||
<a href="https://forms.gle/W3U4PZpJGFHWtHyA9" target="_blank">
|
||||
✨ Make Public
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-6 sm:grid-cols-3 lg:grid-cols-4 pr-5">
|
||||
{modelHubData &&
|
||||
modelHubData.map((model: ModelInfo) => (
|
||||
<Card key={model.model_group} className="mt-5 mx-8">
|
||||
<pre className="flex justify-between">
|
||||
<Title>{model.model_group}</Title>
|
||||
<Tooltip title={model.model_group}>
|
||||
<CopyOutlined
|
||||
onClick={() => copyToClipboard(model.model_group)}
|
||||
style={{ cursor: "pointer", marginRight: "10px" }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</pre>
|
||||
<div className="my-5">
|
||||
<Text>Mode: {model.mode}</Text>
|
||||
<Text>
|
||||
Supports Function Calling:{" "}
|
||||
{model?.supports_function_calling == true ? "Yes" : "No"}
|
||||
</Text>
|
||||
<Text>
|
||||
Supports Vision:{" "}
|
||||
{model?.supports_vision == true ? "Yes" : "No"}
|
||||
</Text>
|
||||
<Text>
|
||||
Max Input Tokens:{" "}
|
||||
{model?.max_input_tokens ? model?.max_input_tokens : "N/A"}
|
||||
</Text>
|
||||
<Text>
|
||||
Max Output Tokens:{" "}
|
||||
{model?.max_output_tokens
|
||||
? model?.max_output_tokens
|
||||
: "N/A"}
|
||||
</Text>
|
||||
</div>
|
||||
<div style={{ marginTop: "auto", textAlign: "right" }}>
|
||||
<a
|
||||
href="#"
|
||||
onClick={() => showModal(model)}
|
||||
style={{ color: "#1890ff", fontSize: "smaller" }}
|
||||
>
|
||||
View more <RightOutlined />
|
||||
<div
|
||||
className={`flex ${publicPage ? "justify-between" : "items-center"}`}
|
||||
>
|
||||
<Title className="ml-8 text-center ">Model Hub</Title>
|
||||
{publicPage == false ? (
|
||||
premiumUser ? (
|
||||
<Button className="ml-4" onClick={() => handleMakePublicPage()}>
|
||||
✨ Make Public
|
||||
</Button>
|
||||
) : (
|
||||
<Button className="ml-4">
|
||||
<a href="https://forms.gle/W3U4PZpJGFHWtHyA9" target="_blank">
|
||||
✨ Make Public
|
||||
</a>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
)
|
||||
) : (
|
||||
<div className="flex justify-between items-center">
|
||||
<p>Filter by key:</p>
|
||||
<Text className="bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center">{`/ui/model_hub?key=<YOUR_KEY>`}</Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-6 sm:grid-cols-3 lg:grid-cols-4">
|
||||
{modelHubData &&
|
||||
modelHubData.map((model: ModelInfo) => (
|
||||
<Card key={model.model_group} className="mt-5 mx-8">
|
||||
<pre className="flex justify-between">
|
||||
<Title>{model.model_group}</Title>
|
||||
<Tooltip title={model.model_group}>
|
||||
<CopyOutlined
|
||||
onClick={() => copyToClipboard(model.model_group)}
|
||||
style={{ cursor: "pointer", marginRight: "10px" }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</pre>
|
||||
<div className="my-5">
|
||||
<Text>Mode: {model.mode}</Text>
|
||||
<Text>
|
||||
Supports Function Calling:{" "}
|
||||
{model?.supports_function_calling == true ? "Yes" : "No"}
|
||||
</Text>
|
||||
<Text>
|
||||
Supports Vision:{" "}
|
||||
{model?.supports_vision == true ? "Yes" : "No"}
|
||||
</Text>
|
||||
<Text>
|
||||
Max Input Tokens:{" "}
|
||||
{model?.max_input_tokens
|
||||
? model?.max_input_tokens
|
||||
: "N/A"}
|
||||
</Text>
|
||||
<Text>
|
||||
Max Output Tokens:{" "}
|
||||
{model?.max_output_tokens
|
||||
? model?.max_output_tokens
|
||||
: "N/A"}
|
||||
</Text>
|
||||
</div>
|
||||
<div style={{ marginTop: "auto", textAlign: "right" }}>
|
||||
<a
|
||||
href="#"
|
||||
onClick={() => showModal(model)}
|
||||
style={{ color: "#1890ff", fontSize: "smaller" }}
|
||||
>
|
||||
View more <RightOutlined />
|
||||
</a>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Card className="mx-auto max-w-xl mt-10">
|
||||
<Text className="text-xl text-center mb-2 text-black">
|
||||
Public Model Hub not enabled.
|
||||
</Text>
|
||||
<p className="text-base text-center text-slate-800">
|
||||
Ask your proxy admin to enable this on their Admin UI.
|
||||
</p>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
title={"Public Model Hub"}
|
||||
width={600}
|
||||
visible={isPublicPageModalVisible}
|
||||
footer={null}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<div className="pt-5 pb-5">
|
||||
<div className="flex justify-between mb-4">
|
||||
<Text className="text-base mr-2">Shareable Link:</Text>
|
||||
<Text className="max-w-sm ml-2 bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center rounded">{`<proxy_base_url>/ui/model_hub?key=<YOUR_API_KEY>`}</Text>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={goToPublicModelPage}>See Page</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
title={
|
||||
selectedModel && selectedModel.model_group
|
||||
|
|
|
|||
|
|
@ -579,17 +579,14 @@ export const modelInfoCall = async (
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
export const modelHubCall = async (
|
||||
accessToken: String,
|
||||
userID: String,
|
||||
userRole: String
|
||||
) => {
|
||||
export const modelHubCall = async (accessToken: String) => {
|
||||
/**
|
||||
* Get all models on proxy
|
||||
*/
|
||||
try {
|
||||
let url = proxyBaseUrl ? `${proxyBaseUrl}/model_group/info` : `/model_group/info`;
|
||||
let url = proxyBaseUrl
|
||||
? `${proxyBaseUrl}/model_group/info`
|
||||
: `/model_group/info`;
|
||||
|
||||
//message.info("Requesting model data");
|
||||
const response = await fetch(url, {
|
||||
|
|
@ -617,8 +614,6 @@ export const modelHubCall = async (
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const modelMetricsCall = async (
|
||||
accessToken: String,
|
||||
userID: String,
|
||||
|
|
@ -1870,6 +1865,38 @@ export const getGeneralSettingsCall = async (accessToken: String) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const getConfigFieldSetting = async (
|
||||
accessToken: String,
|
||||
fieldName: string
|
||||
) => {
|
||||
try {
|
||||
let url = proxyBaseUrl
|
||||
? `${proxyBaseUrl}/config/field/info?field_name=${fieldName}`
|
||||
: `/config/field/info?field_name=${fieldName}`;
|
||||
|
||||
//message.info("Requesting model data");
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.text();
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
// Handle success - you might want to update some state or UI based on the created key
|
||||
} catch (error) {
|
||||
console.error("Failed to set callbacks:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateConfigFieldSetting = async (
|
||||
accessToken: String,
|
||||
fieldName: string,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue