fix(ui): normalize scope in shared guardrail field

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yucheng 2026-09-29 10:57:36 +00:00
parent d3bb9a6a4a
commit ef1a1f42ec
3 changed files with 11 additions and 16 deletions

View file

@ -1,7 +1,7 @@
"use client";
import { CircleHelp } from "lucide-react";
import React, { useId } from "react";
import React, { useEffect, useId } from "react";
import { useController, type Control, type ControllerRenderProps, type RegisterOptions } from "react-hook-form";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
@ -9,6 +9,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
import {
getLoggingOnlyScopeOptions,
modeIncludesLoggingOnly,
normalizeLoggingOnlyScopeChoice,
type LoggingOnlyScopeChoice,
} from "./guardrail_info_helpers";
@ -44,6 +45,9 @@ export const asText = (value: unknown): string => {
return "";
};
const isLoggingOnlyScopeChoice = (value: unknown): value is LoggingOnlyScopeChoice =>
value === "default" || value === "input" || value === "output" || value === "both";
export const asStringArray = (value: unknown): string[] => {
if (Array.isArray(value)) return value.filter((entry): entry is string => typeof entry === "string");
if (typeof value === "string" && value !== "") return [value];
@ -137,6 +141,12 @@ export const LoggingOnlyScopeSelect: React.FC<{
const { id, value, onChange, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy } = control;
const items = getLoggingOnlyScopeOptions(directionalScopeSupported);
useEffect(() => {
const currentChoice = isLoggingOnlyScopeChoice(value) ? value : "default";
const choice = normalizeLoggingOnlyScopeChoice(currentChoice, directionalScopeSupported);
if (choice !== value) onChange(choice);
}, [value, directionalScopeSupported, onChange]);
return (
<Select items={items} value={asText(value) || "default"} onValueChange={onChange}>
<SelectTrigger id={id} aria-invalid={ariaInvalid} aria-describedby={ariaDescribedBy} className="w-full">

View file

@ -18,7 +18,6 @@ import {
getSupportedModesForProvider,
guardrail_provider_map,
modeIncludesLoggingOnly,
normalizeLoggingOnlyScopeChoice,
populateGuardrailProviderMap,
populateGuardrailProviders,
shouldRenderContentFilterConfigSettings,
@ -246,12 +245,6 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
}, [selectedProvider]);
const directionalScopeSupported = supportsDirectionalLoggingOnlyScope(guardrailSettings, selectedProvider);
useEffect(() => {
const scopeChoice = form.getValues("logging_only_scope_choice");
const normalizedScopeChoice = normalizeLoggingOnlyScopeChoice(scopeChoice, directionalScopeSupported);
if (normalizedScopeChoice !== scopeChoice) form.setValue("logging_only_scope_choice", normalizedScopeChoice);
}, [directionalScopeSupported, form]);
// Fetch guardrail UI settings + provider params on mount / accessToken change
useEffect(() => {
if (!accessToken) return;

View file

@ -39,7 +39,6 @@ import {
getGuardrailLogoAndName,
guardrail_provider_map,
loggingOnlyScopeToChoice,
normalizeLoggingOnlyScopeChoice,
skipSystemMessageToChoice,
skipToolMessageToChoice,
supportsDirectionalLoggingOnlyScope,
@ -246,12 +245,6 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
}
}, [guardrailData, guardrailProviderSpecificParams, form]);
useEffect(() => {
const scopeChoice = form.getValues("logging_only_scope_choice");
const normalizedScopeChoice = normalizeLoggingOnlyScopeChoice(scopeChoice, directionalScopeSupported);
if (normalizedScopeChoice !== scopeChoice) form.setValue("logging_only_scope_choice", normalizedScopeChoice);
}, [directionalScopeSupported, form, guardrailData]);
const resetToolPermissionEditor = useCallback(() => {
if (guardrailData?.litellm_params?.guardrail === "tool_permission") {
setToolPermissionConfig({
@ -525,7 +518,6 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
const isConfigGuardrail = guardrailData.guardrail_definition_location === "config";
/* eslint-disable max-lines -- keep edit-form scope normalization with its owning view */
return (
<div className="p-4">
<div>