mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-26 11:14:04 +00:00
Merge pull request #2902 from BerriAI/litellm_ui_set_get_callbacks
UI view set callbacks
This commit is contained in:
commit
0d925a6c55
5 changed files with 211 additions and 27 deletions
|
@ -1809,17 +1809,15 @@ class ProxyConfig:
|
||||||
}
|
}
|
||||||
|
|
||||||
## DB
|
## DB
|
||||||
if (
|
if prisma_client is not None and (
|
||||||
prisma_client is not None
|
general_settings.get("store_model_in_db", False) == True
|
||||||
and litellm.get_secret("SAVE_CONFIG_TO_DB", False) == True
|
|
||||||
):
|
):
|
||||||
prisma_setup(database_url=None) # in case it's not been connected yet
|
|
||||||
_tasks = []
|
_tasks = []
|
||||||
keys = [
|
keys = [
|
||||||
"model_list",
|
|
||||||
"general_settings",
|
"general_settings",
|
||||||
"router_settings",
|
"router_settings",
|
||||||
"litellm_settings",
|
"litellm_settings",
|
||||||
|
"environment_variables",
|
||||||
]
|
]
|
||||||
for k in keys:
|
for k in keys:
|
||||||
response = prisma_client.get_generic_data(
|
response = prisma_client.get_generic_data(
|
||||||
|
@ -1828,6 +1826,12 @@ class ProxyConfig:
|
||||||
_tasks.append(response)
|
_tasks.append(response)
|
||||||
|
|
||||||
responses = await asyncio.gather(*_tasks)
|
responses = await asyncio.gather(*_tasks)
|
||||||
|
for response in responses:
|
||||||
|
if response is not None:
|
||||||
|
param_name = getattr(response, "param_name", None)
|
||||||
|
param_value = getattr(response, "param_value", None)
|
||||||
|
if param_name is not None and param_value is not None:
|
||||||
|
config[param_name] = param_value
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
@ -1836,11 +1840,6 @@ class ProxyConfig:
|
||||||
# Load existing config
|
# Load existing config
|
||||||
backup_config = await self.get_config()
|
backup_config = await self.get_config()
|
||||||
|
|
||||||
# Save the updated config
|
|
||||||
## YAML
|
|
||||||
with open(f"{user_config_file_path}", "w") as config_file:
|
|
||||||
yaml.dump(new_config, config_file, default_flow_style=False)
|
|
||||||
|
|
||||||
# update Router - verifies if this is a valid config
|
# update Router - verifies if this is a valid config
|
||||||
try:
|
try:
|
||||||
(
|
(
|
||||||
|
@ -1862,22 +1861,17 @@ class ProxyConfig:
|
||||||
- Do not write restricted params like 'api_key' to the database
|
- Do not write restricted params like 'api_key' to the database
|
||||||
- if api_key is passed, save that to the local environment or connected secret manage (maybe expose `litellm.save_secret()`)
|
- if api_key is passed, save that to the local environment or connected secret manage (maybe expose `litellm.save_secret()`)
|
||||||
"""
|
"""
|
||||||
if (
|
if prisma_client is not None and (
|
||||||
prisma_client is not None
|
general_settings.get("store_model_in_db", False) == True
|
||||||
and litellm.get_secret("SAVE_CONFIG_TO_DB", default_value=False) == True
|
|
||||||
):
|
):
|
||||||
### KEY REMOVAL ###
|
# if using - db for config - models are in ModelTable
|
||||||
models = new_config.get("model_list", [])
|
new_config.pop("model_list", None)
|
||||||
for m in models:
|
|
||||||
if m.get("litellm_params", {}).get("api_key", None) is not None:
|
|
||||||
# pop the key
|
|
||||||
api_key = m["litellm_params"].pop("api_key")
|
|
||||||
# store in local env
|
|
||||||
key_name = f"LITELLM_MODEL_KEY_{uuid.uuid4()}"
|
|
||||||
os.environ[key_name] = api_key
|
|
||||||
# save the key name (not the value)
|
|
||||||
m["litellm_params"]["api_key"] = f"os.environ/{key_name}"
|
|
||||||
await prisma_client.insert_data(data=new_config, table_name="config")
|
await prisma_client.insert_data(data=new_config, table_name="config")
|
||||||
|
else:
|
||||||
|
# Save the updated config - if user is not using a dB
|
||||||
|
## YAML
|
||||||
|
with open(f"{user_config_file_path}", "w") as config_file:
|
||||||
|
yaml.dump(new_config, config_file, default_flow_style=False)
|
||||||
|
|
||||||
async def load_team_config(self, team_id: str):
|
async def load_team_config(self, team_id: str):
|
||||||
"""
|
"""
|
||||||
|
@ -7922,8 +7916,10 @@ async def update_config(config_info: ConfigYAML):
|
||||||
|
|
||||||
Currently supports modifying General Settings + LiteLLM settings
|
Currently supports modifying General Settings + LiteLLM settings
|
||||||
"""
|
"""
|
||||||
global llm_router, llm_model_list, general_settings, proxy_config, proxy_logging_obj
|
global llm_router, llm_model_list, general_settings, proxy_config, proxy_logging_obj, master_key
|
||||||
try:
|
try:
|
||||||
|
import base64
|
||||||
|
|
||||||
# Load existing config
|
# Load existing config
|
||||||
config = await proxy_config.get_config()
|
config = await proxy_config.get_config()
|
||||||
|
|
||||||
|
@ -7943,9 +7939,17 @@ async def update_config(config_info: ConfigYAML):
|
||||||
|
|
||||||
if config_info.environment_variables is not None:
|
if config_info.environment_variables is not None:
|
||||||
config.setdefault("environment_variables", {})
|
config.setdefault("environment_variables", {})
|
||||||
updated_environment_variables = config_info.environment_variables
|
_updated_environment_variables = config_info.environment_variables
|
||||||
|
|
||||||
|
# encrypt updated_environment_variables #
|
||||||
|
for k, v in _updated_environment_variables.items():
|
||||||
|
if isinstance(v, str):
|
||||||
|
encrypted_value = encrypt_value(value=v, master_key=master_key) # type: ignore
|
||||||
|
_updated_environment_variables[k] = base64.b64encode(
|
||||||
|
encrypted_value
|
||||||
|
).decode("utf-8")
|
||||||
config["environment_variables"] = {
|
config["environment_variables"] = {
|
||||||
**updated_environment_variables,
|
**_updated_environment_variables,
|
||||||
**config["environment_variables"],
|
**config["environment_variables"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7958,6 +7962,25 @@ async def update_config(config_info: ConfigYAML):
|
||||||
**config["litellm_settings"],
|
**config["litellm_settings"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# if litellm.success_callback in updated_litellm_settings and config["litellm_settings"]
|
||||||
|
if (
|
||||||
|
"success_callback" in updated_litellm_settings
|
||||||
|
and "success_callback" in config["litellm_settings"]
|
||||||
|
):
|
||||||
|
|
||||||
|
# check both success callback are lists
|
||||||
|
if isinstance(
|
||||||
|
config["litellm_settings"]["success_callback"], list
|
||||||
|
) and isinstance(updated_litellm_settings["success_callback"], list):
|
||||||
|
combined_success_callback = (
|
||||||
|
config["litellm_settings"]["success_callback"]
|
||||||
|
+ updated_litellm_settings["success_callback"]
|
||||||
|
)
|
||||||
|
combined_success_callback = list(set(combined_success_callback))
|
||||||
|
config["litellm_settings"][
|
||||||
|
"success_callback"
|
||||||
|
] = combined_success_callback
|
||||||
|
|
||||||
# Save the updated config
|
# Save the updated config
|
||||||
await proxy_config.save_config(new_config=config)
|
await proxy_config.save_config(new_config=config)
|
||||||
|
|
||||||
|
@ -7987,6 +8010,48 @@ async def update_config(config_info: ConfigYAML):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(
|
||||||
|
"/get/config/callbacks",
|
||||||
|
tags=["config.yaml"],
|
||||||
|
include_in_schema=False,
|
||||||
|
dependencies=[Depends(user_api_key_auth)],
|
||||||
|
)
|
||||||
|
async def get_config():
|
||||||
|
"""
|
||||||
|
For Admin UI - allows admin to view config via UI
|
||||||
|
|
||||||
|
"""
|
||||||
|
global llm_router, llm_model_list, general_settings, proxy_config, proxy_logging_obj, master_key
|
||||||
|
try:
|
||||||
|
|
||||||
|
config_data = await proxy_config.get_config()
|
||||||
|
_environment_variables = config_data.get("environment_variables", {})
|
||||||
|
config_data = config_data["litellm_settings"]
|
||||||
|
|
||||||
|
# only store the keys and return the values as sk...***
|
||||||
|
for key, value in _environment_variables.items():
|
||||||
|
_environment_variables[key] = value[:5] + "*****"
|
||||||
|
config_data["environment_variables"] = _environment_variables
|
||||||
|
return {"data": config_data, "status": "success"}
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
if isinstance(e, HTTPException):
|
||||||
|
raise ProxyException(
|
||||||
|
message=getattr(e, "detail", f"Authentication Error({str(e)})"),
|
||||||
|
type="auth_error",
|
||||||
|
param=getattr(e, "param", "None"),
|
||||||
|
code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST),
|
||||||
|
)
|
||||||
|
elif isinstance(e, ProxyException):
|
||||||
|
raise e
|
||||||
|
raise ProxyException(
|
||||||
|
message="Authentication Error, " + str(e),
|
||||||
|
type="auth_error",
|
||||||
|
param=getattr(e, "param", "None"),
|
||||||
|
code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/config/yaml",
|
"/config/yaml",
|
||||||
tags=["config.yaml"],
|
tags=["config.yaml"],
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ModelDashboard from "@/components/model_dashboard";
|
||||||
import ViewUserDashboard from "@/components/view_users";
|
import ViewUserDashboard from "@/components/view_users";
|
||||||
import Teams from "@/components/teams";
|
import Teams from "@/components/teams";
|
||||||
import AdminPanel from "@/components/admins";
|
import AdminPanel from "@/components/admins";
|
||||||
|
import Settings from "@/components/settings";
|
||||||
import ChatUI from "@/components/chat_ui";
|
import ChatUI from "@/components/chat_ui";
|
||||||
import Sidebar from "../components/leftnav";
|
import Sidebar from "../components/leftnav";
|
||||||
import Usage from "../components/usage";
|
import Usage from "../components/usage";
|
||||||
|
@ -161,6 +162,12 @@ const CreateKeyPage = () => {
|
||||||
searchParams={searchParams}
|
searchParams={searchParams}
|
||||||
accessToken={accessToken}
|
accessToken={accessToken}
|
||||||
/>
|
/>
|
||||||
|
) : page == "settings" ? (
|
||||||
|
<Settings
|
||||||
|
userID={userID}
|
||||||
|
userRole={userRole}
|
||||||
|
accessToken={accessToken}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Usage
|
<Usage
|
||||||
userID={userID}
|
userID={userID}
|
||||||
|
|
|
@ -87,6 +87,11 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
Models
|
Models
|
||||||
</Text>
|
</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Item key="8" onClick={() => setPage("settings")}>
|
||||||
|
<Text>
|
||||||
|
Settings
|
||||||
|
</Text>
|
||||||
|
</Menu.Item>
|
||||||
{userRole == "Admin" ? (
|
{userRole == "Admin" ? (
|
||||||
<Menu.Item key="7" onClick={() => setPage("admin-panel")}>
|
<Menu.Item key="7" onClick={() => setPage("admin-panel")}>
|
||||||
<Text>
|
<Text>
|
||||||
|
|
|
@ -1120,3 +1120,42 @@ export const slackBudgetAlertsHealthCheck = async (accessToken: String) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const getCallbacksCall = async (
|
||||||
|
accessToken: String,
|
||||||
|
userID: String,
|
||||||
|
userRole: String
|
||||||
|
) => {
|
||||||
|
/**
|
||||||
|
* Get all the models user has access to
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
let url = proxyBaseUrl ? `${proxyBaseUrl}/get/config/callbacks` : `/get/config/callbacks`;
|
||||||
|
|
||||||
|
//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();
|
||||||
|
message.error(errorData);
|
||||||
|
throw new Error("Network response was not ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
//message.info("Received model data");
|
||||||
|
return data;
|
||||||
|
// Handle success - you might want to update some state or UI based on the created key
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get callbacks:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
68
ui/litellm-dashboard/src/components/settings.tsx
Normal file
68
ui/litellm-dashboard/src/components/settings.tsx
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { Card, Title, Subtitle, Table, TableHead, TableRow, Badge, TableHeaderCell, TableCell, TableBody, Metric, Text, Grid, Button, Col, } from "@tremor/react";
|
||||||
|
import { getCallbacksCall } from "./networking";
|
||||||
|
|
||||||
|
interface SettingsPageProps {
|
||||||
|
accessToken: string | null;
|
||||||
|
userRole: string | null;
|
||||||
|
userID: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Settings: React.FC<SettingsPageProps> = ({
|
||||||
|
accessToken,
|
||||||
|
userRole,
|
||||||
|
userID,
|
||||||
|
}) => {
|
||||||
|
const [callbacks, setCallbacks] = useState(["None"]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accessToken || !userRole || !userID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getCallbacksCall(accessToken, userID, userRole).then((data) => {
|
||||||
|
console.log("callbacks",data);
|
||||||
|
let callbacks_data = data.data;
|
||||||
|
let callback_names = callbacks_data.success_callback // ["callback1", "callback2"]
|
||||||
|
|
||||||
|
setCallbacks(callback_names);
|
||||||
|
});
|
||||||
|
}, [accessToken, userRole, userID]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full mx-4">
|
||||||
|
<Grid numItems={1} className="gap-2 p-8 h-[75vh] w-full mt-2">
|
||||||
|
|
||||||
|
<Card className="h-[15vh]">
|
||||||
|
|
||||||
|
<Grid numItems={2} className="mt-2">
|
||||||
|
<Col>
|
||||||
|
<Title>Logging Callbacks</Title>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<div>
|
||||||
|
{callbacks.length === 0 ? (
|
||||||
|
<Badge>None</Badge>
|
||||||
|
) : (
|
||||||
|
callbacks.map((callback, index) => (
|
||||||
|
<Badge key={index} color={"sky"}>
|
||||||
|
{callback}
|
||||||
|
</Badge>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
<Col>
|
||||||
|
<Button size="xs" className="mt-2">Add Callback</Button>
|
||||||
|
</Col>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Settings;
|
Loading…
Add table
Add a link
Reference in a new issue