mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
[UI] Allow editing guardrails (#10907)
* ui qa guardrails * ui fixes guardrail info * feat: add patch guardrail endpoint * fix: update_guardrail_in_db * ui add support for editing guardrails
This commit is contained in:
parent
d8525ecbfa
commit
76de6e374c
6 changed files with 185 additions and 43 deletions
|
|
@ -19,6 +19,7 @@ from litellm.types.guardrails import (
|
|||
GuardrailUIAddGuardrailSettings,
|
||||
LakeraV2GuardrailConfigModel,
|
||||
ListGuardrailsResponse,
|
||||
PatchGuardrailRequest,
|
||||
PiiAction,
|
||||
PiiEntityType,
|
||||
PresidioConfigModel,
|
||||
|
|
@ -434,6 +435,115 @@ async def delete_guardrail(guardrail_id: str):
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.patch(
|
||||
"/guardrails/{guardrail_id}",
|
||||
tags=["Guardrails"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def patch_guardrail(guardrail_id: str, request: PatchGuardrailRequest):
|
||||
"""
|
||||
Partially update an existing guardrail
|
||||
|
||||
👉 [Guardrail docs](https://docs.litellm.ai/docs/proxy/guardrails/quick_start)
|
||||
|
||||
This endpoint allows updating specific fields of a guardrail without sending the entire object.
|
||||
Only the following fields can be updated:
|
||||
- guardrail_name: The name of the guardrail
|
||||
- default_on: Whether the guardrail is enabled by default
|
||||
- guardrail_info: Additional information about the guardrail
|
||||
|
||||
Example Request:
|
||||
```bash
|
||||
curl -X PATCH "http://localhost:4000/guardrails/123e4567-e89b-12d3-a456-426614174000" \\
|
||||
-H "Authorization: Bearer <your_api_key>" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{
|
||||
"guardrail_name": "updated-name",
|
||||
"default_on": true,
|
||||
"guardrail_info": {
|
||||
"description": "Updated description"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Example Response:
|
||||
```json
|
||||
{
|
||||
"guardrail_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"guardrail_name": "updated-name",
|
||||
"litellm_params": {
|
||||
"guardrail": "bedrock",
|
||||
"mode": "pre_call",
|
||||
"guardrailIdentifier": "ff6ujrregl1q",
|
||||
"guardrailVersion": "DRAFT",
|
||||
"default_on": true
|
||||
},
|
||||
"guardrail_info": {
|
||||
"description": "Updated description"
|
||||
},
|
||||
"created_at": "2023-11-09T12:34:56.789Z",
|
||||
"updated_at": "2023-11-09T14:22:33.456Z"
|
||||
}
|
||||
```
|
||||
"""
|
||||
from litellm.proxy.proxy_server import prisma_client
|
||||
|
||||
if prisma_client is None:
|
||||
raise HTTPException(status_code=500, detail="Prisma client not initialized")
|
||||
|
||||
try:
|
||||
# Check if guardrail exists and get current data
|
||||
existing_guardrail = await GUARDRAIL_REGISTRY.get_guardrail_by_id_from_db(
|
||||
guardrail_id=guardrail_id, prisma_client=prisma_client
|
||||
)
|
||||
|
||||
if existing_guardrail is None:
|
||||
raise HTTPException(
|
||||
status_code=404, detail=f"Guardrail with ID {guardrail_id} not found"
|
||||
)
|
||||
|
||||
# Create updated guardrail object
|
||||
guardrail_name = (
|
||||
request.guardrail_name
|
||||
if request.guardrail_name is not None
|
||||
else existing_guardrail.get("guardrail_name")
|
||||
)
|
||||
|
||||
# Update litellm_params if default_on is provided
|
||||
litellm_params = dict(existing_guardrail.get("litellm_params", {}))
|
||||
if (
|
||||
request.litellm_params is not None
|
||||
and request.litellm_params.default_on is not None
|
||||
):
|
||||
litellm_params["default_on"] = request.litellm_params.default_on
|
||||
|
||||
# Update guardrail_info if provided
|
||||
guardrail_info = (
|
||||
request.guardrail_info
|
||||
if request.guardrail_info is not None
|
||||
else existing_guardrail.get("guardrail_info", {})
|
||||
)
|
||||
|
||||
# Create the guardrail object
|
||||
updated_guardrail = {
|
||||
"guardrail_name": guardrail_name,
|
||||
"litellm_params": litellm_params,
|
||||
"guardrail_info": guardrail_info,
|
||||
}
|
||||
|
||||
result = await GUARDRAIL_REGISTRY.update_guardrail_in_db(
|
||||
guardrail_id=guardrail_id,
|
||||
guardrail=Guardrail(**updated_guardrail),
|
||||
prisma_client=prisma_client,
|
||||
)
|
||||
return result
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
verbose_proxy_logger.exception(f"Error updating guardrail: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get(
|
||||
"/guardrails/{guardrail_id}/info",
|
||||
tags=["Guardrails"],
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ class GuardrailRegistry:
|
|||
try:
|
||||
guardrail_name = guardrail.get("guardrail_name")
|
||||
litellm_params: str = safe_dumps(dict(guardrail.get("litellm_params", {})))
|
||||
guardrail_info = guardrail.get("guardrail_info", {})
|
||||
guardrail_info: str = safe_dumps(guardrail.get("guardrail_info", {}))
|
||||
|
||||
# Update in DB
|
||||
updated_guardrail = await prisma_client.db.litellm_guardrailstable.update(
|
||||
|
|
|
|||
|
|
@ -432,3 +432,13 @@ class ApplyGuardrailRequest(BaseModel):
|
|||
|
||||
class ApplyGuardrailResponse(BaseModel):
|
||||
response_text: str
|
||||
|
||||
|
||||
class PatchGuardrailLitellmParams(BaseModel):
|
||||
default_on: Optional[bool] = None
|
||||
|
||||
|
||||
class PatchGuardrailRequest(BaseModel):
|
||||
guardrail_name: Optional[str] = None
|
||||
litellm_params: Optional[PatchGuardrailLitellmParams] = None
|
||||
guardrail_info: Optional[Dict[str, Any]] = None
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ import {
|
|||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
TextInput,
|
||||
} from "@tremor/react";
|
||||
import { Button, Form, Input, Select, message, Tooltip } from "antd";
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { getGuardrailInfo } from "@/components/networking";
|
||||
import { getGuardrailInfo, updateGuardrailCall } from "@/components/networking";
|
||||
import { getGuardrailLogoAndName } from "./guardrail_info_helpers";
|
||||
|
||||
export interface GuardrailInfoProps {
|
||||
|
|
@ -56,11 +57,23 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({
|
|||
|
||||
const handleGuardrailUpdate = async (values: any) => {
|
||||
try {
|
||||
// Not implemented yet - will be added in the future
|
||||
message.info("Guardrail update functionality coming soon");
|
||||
if (!accessToken) return;
|
||||
|
||||
const updateData = {
|
||||
guardrail_name: values.guardrail_name,
|
||||
litellm_params: {
|
||||
default_on: values.default_on
|
||||
},
|
||||
guardrail_info: values.guardrail_info ? JSON.parse(values.guardrail_info) : undefined
|
||||
};
|
||||
|
||||
await updateGuardrailCall(accessToken, guardrailId, updateData);
|
||||
message.success("Guardrail updated successfully");
|
||||
fetchGuardrailInfo();
|
||||
setIsEditing(false);
|
||||
} catch (error) {
|
||||
console.error("Error updating guardrail:", error);
|
||||
message.error("Failed to update guardrail");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -139,30 +152,9 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({
|
|||
</Card>
|
||||
</Grid>
|
||||
|
||||
<Card className="mt-6">
|
||||
<Text>Provider Configuration</Text>
|
||||
<div className="mt-2 space-y-2">
|
||||
{Object.entries(guardrailData.litellm_params || {}).map(([key, value]) => {
|
||||
// Skip mode and guardrail as they're displayed above
|
||||
if (key === 'mode' || key === 'guardrail' || key === 'default_on') return null;
|
||||
|
||||
return (
|
||||
<div key={key} className="flex">
|
||||
<Text className="font-medium w-1/3">{key}</Text>
|
||||
<Text className="w-2/3">
|
||||
{typeof value === 'object'
|
||||
? JSON.stringify(value, null, 2)
|
||||
: String(value)}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{guardrailData.guardrail_info && Object.keys(guardrailData.guardrail_info).length > 0 && (
|
||||
<Card className="mt-6">
|
||||
<Text>Additional Information</Text>
|
||||
<Text>Guardrail Info</Text>
|
||||
<div className="mt-2 space-y-2">
|
||||
{Object.entries(guardrailData.guardrail_info).map(([key, value]) => (
|
||||
<div key={key} className="flex">
|
||||
|
|
@ -212,7 +204,7 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({
|
|||
name="guardrail_name"
|
||||
rules={[{ required: true, message: "Please input a guardrail name" }]}
|
||||
>
|
||||
<Input />
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
|
|
@ -226,7 +218,7 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({
|
|||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Additional Information"
|
||||
label="Guardrail Information"
|
||||
name="guardrail_info"
|
||||
>
|
||||
<Input.TextArea rows={5} />
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import {
|
|||
SwitchVerticalIcon,
|
||||
ChevronUpIcon,
|
||||
ChevronDownIcon,
|
||||
PencilIcon,
|
||||
} from "@heroicons/react/outline";
|
||||
import { Tooltip } from "antd";
|
||||
import {
|
||||
|
|
@ -210,13 +209,6 @@ const GuardrailTable: React.FC<GuardrailTableProps> = ({
|
|||
const guardrail = row.original;
|
||||
return (
|
||||
<div className="flex space-x-2">
|
||||
<Icon
|
||||
icon={PencilIcon}
|
||||
size="sm"
|
||||
onClick={() => guardrail.guardrail_id && handleEditClick(guardrail)}
|
||||
className="cursor-pointer hover:text-blue-500"
|
||||
tooltip="Edit guardrail"
|
||||
/>
|
||||
<Icon
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
|
|
|
|||
|
|
@ -5046,7 +5046,7 @@ export { type Team } from "./key_team_helpers/key_list"; // Re-export Team
|
|||
|
||||
export const deleteGuardrailCall = async (accessToken: string, guardrailId: string) => {
|
||||
try {
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/guardrails/${guardrailId}` : `/guardrails/${guardrailId}`;
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/v2/guardrails/${guardrailId}` : `/v2/guardrails/${guardrailId}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "DELETE",
|
||||
|
|
@ -5126,26 +5126,64 @@ export const getGuardrailProviderSpecificParams = async (accessToken: string) =>
|
|||
};
|
||||
|
||||
export const getGuardrailInfo = async (accessToken: string, guardrailId: string) => {
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/guardrails/${guardrailId}/info` : `/guardrails/${guardrailId}/info`;
|
||||
|
||||
try {
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/guardrails/${guardrailId}/info` : `/guardrails/${guardrailId}/info`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
...(accessToken ? { "Authorization": `Bearer ${accessToken}` } : {})
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.text();
|
||||
throw new Error(errorData);
|
||||
handleError(errorData);
|
||||
throw new Error("Failed to get guardrail info");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("Guardrail info response:", data);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching guardrail info:", error);
|
||||
console.error("Failed to get guardrail info:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateGuardrailCall = async (
|
||||
accessToken: string,
|
||||
guardrailId: string,
|
||||
updateData: {
|
||||
guardrail_name?: string;
|
||||
default_on?: boolean;
|
||||
guardrail_info?: Record<string, any>;
|
||||
}
|
||||
) => {
|
||||
try {
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/guardrails/${guardrailId}` : `/guardrails/${guardrailId}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(updateData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.text();
|
||||
handleError(errorData);
|
||||
throw new Error("Failed to update guardrail");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("Update guardrail response:", data);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Failed to update guardrail:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue