diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/custom_code/CustomCodeLoggingOnlyScope.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/custom_code/CustomCodeLoggingOnlyScope.tsx new file mode 100644 index 00000000000..8239933d649 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/custom_code/CustomCodeLoggingOnlyScope.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { + choiceToLoggingOnlyScope, + getLoggingOnlyScopeOptions, + getLoggingOnlyScopeUpdate, + type LoggingOnlyScope, + type LoggingOnlyScopeChoice, +} from "../guardrail_info_helpers"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; + +const LOGGING_ONLY_SCOPE_ITEMS = getLoggingOnlyScopeOptions(true); +const LOGGING_ONLY_SCOPE_CHOICES: ReadonlySet = new Set(LOGGING_ONLY_SCOPE_ITEMS.map(({ value }) => value)); + +const isLoggingOnlyScopeChoice = (value: unknown): value is LoggingOnlyScopeChoice => + typeof value === "string" && LOGGING_ONLY_SCOPE_CHOICES.has(value); + +export const CustomCodeLoggingOnlyScopeSelect: React.FC<{ + value: LoggingOnlyScopeChoice; + onChange: (choice: LoggingOnlyScopeChoice) => void; +}> = ({ value, onChange }) => ( +
+ + +
+); + +export const getCustomCodeLoggingOnlyScopeCreate = ( + mode: string[], + choice: LoggingOnlyScopeChoice, +): { logging_only_scope?: LoggingOnlyScope } => { + if (!mode.includes("logging_only")) return {}; + const scope = choiceToLoggingOnlyScope(choice); + return scope === null ? {} : { logging_only_scope: scope }; +}; + +export const getCustomCodeLoggingOnlyScopeUpdate = ( + mode: string[], + litellmParams: { logging_only_scope?: string | null } | null | undefined, + choice: LoggingOnlyScopeChoice, +): { logging_only_scope?: LoggingOnlyScope | null } => + mode.includes("logging_only") ? getLoggingOnlyScopeUpdate(litellmParams, choice) : {}; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/custom_code/CustomCodeModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/custom_code/CustomCodeModal.tsx index 29523e9e294..9deef4aeee3 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/custom_code/CustomCodeModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/custom_code/CustomCodeModal.tsx @@ -2,14 +2,13 @@ import React, { useState, useRef, useEffect } from "react"; import { CheckCircle2, ChevronRight, Code, ExternalLink, PlayCircle, Save, Users, XCircle } from "lucide-react"; import { createGuardrailCall, updateGuardrailCall, testCustomCodeGuardrail } from "@/components/networking"; import { toast } from "@/lib/toast"; +import { loggingOnlyScopeToChoice } from "../guardrail_info_helpers"; +import type { LoggingOnlyScope, LoggingOnlyScopeChoice } from "../guardrail_info_helpers"; import { - choiceToLoggingOnlyScope, - getLoggingOnlyScopeOptions, - getLoggingOnlyScopeUpdate, - loggingOnlyScopeToChoice, - type LoggingOnlyScope, - type LoggingOnlyScopeChoice, -} from "../guardrail_info_helpers"; + CustomCodeLoggingOnlyScopeSelect, + getCustomCodeLoggingOnlyScopeCreate, + getCustomCodeLoggingOnlyScopeUpdate, +} from "./CustomCodeLoggingOnlyScope"; import { Button } from "@/components/ui/button"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { @@ -172,8 +171,6 @@ const TEMPLATE_ITEMS = Object.entries(CODE_TEMPLATES).map(([key, template]) => ( label: template.name, })); -const LOGGING_ONLY_SCOPE_ITEMS = getLoggingOnlyScopeOptions(true); - type ModeOption = (typeof MODE_OPTIONS)[number]; const MODE_OPTION_BY_VALUE: Record = Object.fromEntries( @@ -202,7 +199,6 @@ interface CustomCodeModalProps { editData?: EditGuardrailData | null; } -/* eslint-disable max-lines -- The existing modal remains the requested location for this scope control */ const CustomCodeModal: React.FC = ({ visible, onClose, onSuccess, accessToken, editData }) => { const anchor = useComboboxAnchor(); const isEditMode = !!editData; @@ -399,6 +395,7 @@ const CustomCodeModal: React.FC = ({ visible, onClose, onS const updateData: any = { litellm_params: { custom_code: code, + ...getCustomCodeLoggingOnlyScopeUpdate(mode, editData.litellm_params, loggingOnlyScopeChoice), }, }; @@ -414,17 +411,11 @@ const CustomCodeModal: React.FC = ({ visible, onClose, onS if (defaultOn !== editData.litellm_params?.default_on) { updateData.litellm_params.default_on = defaultOn; } - if (mode.includes("logging_only")) { - Object.assign( - updateData.litellm_params, - getLoggingOnlyScopeUpdate(editData.litellm_params, loggingOnlyScopeChoice), - ); - } + await updateGuardrailCall(accessToken, editData.guardrail_id, updateData); toast.success("Custom code guardrail updated successfully"); } else { // Create new guardrail - const loggingOnlyScope = choiceToLoggingOnlyScope(loggingOnlyScopeChoice); const guardrailData = { guardrail_name: guardrailName, litellm_params: { @@ -432,9 +423,7 @@ const CustomCodeModal: React.FC = ({ visible, onClose, onS mode: mode, default_on: defaultOn, custom_code: code, - ...(mode.includes("logging_only") && loggingOnlyScope !== null - ? { logging_only_scope: loggingOnlyScope } - : {}), + ...getCustomCodeLoggingOnlyScopeCreate(mode, loggingOnlyScopeChoice), }, guardrail_info: {}, }; @@ -572,27 +561,7 @@ const CustomCodeModal: React.FC = ({ visible, onClose, onS {mode.includes("logging_only") && ( -
- - -
+ )}