+ {/* License expiration section */}
+ {licenseInfo?.has_license && licenseInfo.expiration_date && (
+
+ )}
+
{/* Users section */}
{data.total_users !== null && (
{
+ it("should render", () => {
+ render(
);
+
+ expect(screen.getByText("Routes Configuration")).toBeInTheDocument();
+ });
+
+ it("should display Add Route button", () => {
+ render(
);
+
+ expect(screen.getByRole("button", { name: /add route/i })).toBeInTheDocument();
+ });
+
+ it("should show empty state when no routes are configured", () => {
+ render(
);
+
+ expect(screen.getByText(/no routes configured/i)).toBeInTheDocument();
+ });
+
+ it("should add a route when Add Route is clicked", async () => {
+ const user = userEvent.setup();
+ render(
);
+
+ await user.click(screen.getByRole("button", { name: /add route/i }));
+
+ expect(screen.getByText("Route 1: Unnamed")).toBeInTheDocument();
+ });
+
+ it("should call onChange when a route is added", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render(
);
+
+ await user.click(screen.getByRole("button", { name: /add route/i }));
+
+ expect(onChange).toHaveBeenCalledWith({
+ routes: [
+ expect.objectContaining({
+ name: "",
+ utterances: [],
+ description: "",
+ score_threshold: 0.5,
+ }),
+ ],
+ });
+ });
+
+ it("should initialize routes from value prop", async () => {
+ const value = {
+ routes: [
+ {
+ name: "gpt-4",
+ utterances: ["hello", "hi"],
+ description: "For greetings",
+ score_threshold: 0.7,
+ },
+ ],
+ };
+ render(
);
+
+ await waitFor(() => {
+ expect(screen.getByText("Route 1: gpt-4")).toBeInTheDocument();
+ });
+ });
+
+ it("should support both name and model fields in value prop", async () => {
+ const value = {
+ routes: [{ model: "gpt-3.5-turbo", utterances: [], description: "", score_threshold: 0.5 }],
+ };
+ render(
);
+
+ await waitFor(() => {
+ expect(screen.getByText("Route 1: gpt-3.5-turbo")).toBeInTheDocument();
+ });
+ });
+
+ it("should remove a route when delete button is clicked", async () => {
+ const user = userEvent.setup();
+ const value = {
+ routes: [
+ {
+ name: "gpt-4",
+ utterances: [],
+ description: "",
+ score_threshold: 0.5,
+ },
+ ],
+ };
+ render(
);
+
+ await waitFor(() => {
+ expect(screen.getByText("Route 1: gpt-4")).toBeInTheDocument();
+ });
+
+ const deleteButton = screen.getByRole("button", { name: "delete" });
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.queryByText("Route 1: gpt-4")).not.toBeInTheDocument();
+ expect(screen.getByText(/no routes configured/i)).toBeInTheDocument();
+ });
+ });
+
+ it("should call onChange when route is removed", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ const value = {
+ routes: [
+ {
+ name: "gpt-4",
+ utterances: [],
+ description: "",
+ score_threshold: 0.5,
+ },
+ ],
+ };
+ render(
);
+
+ await waitFor(() => {
+ expect(screen.getByText("Route 1: gpt-4")).toBeInTheDocument();
+ });
+
+ const deleteButton = screen.getByRole("button", { name: "delete" });
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(onChange).toHaveBeenCalledWith({ routes: [] });
+ });
+ });
+
+
+
+
+ it("should update route when description is changed", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ const value = {
+ routes: [
+ {
+ name: "gpt-4",
+ utterances: [],
+ description: "",
+ score_threshold: 0.5,
+ },
+ ],
+ };
+ render(
);
+
+ await waitFor(() => {
+ expect(screen.getByText("Route 1: gpt-4")).toBeInTheDocument();
+ });
+
+ const descriptionInput = screen.getByPlaceholderText("Describe when this route should be used...");
+ await user.type(descriptionInput, "For code generation");
+
+ await waitFor(() => {
+ const lastCall = onChange.mock.calls[onChange.mock.calls.length - 1];
+ expect(lastCall[0].routes[0].description).toBe("For code generation");
+ });
+ });
+
+ it("should update route when score threshold is changed", async () => {
+ const onChange = vi.fn();
+ const value = {
+ routes: [
+ {
+ name: "gpt-4",
+ utterances: [],
+ description: "",
+ score_threshold: 0.5,
+ },
+ ],
+ };
+ render(
);
+
+ await waitFor(() => {
+ expect(screen.getByText("Route 1: gpt-4")).toBeInTheDocument();
+ });
+
+ const scoreInput = screen.getByRole("spinbutton");
+ fireEvent.change(scoreInput, { target: { value: "0.9" } });
+
+ await waitFor(() => {
+ const lastCall = onChange.mock.calls[onChange.mock.calls.length - 1];
+ expect(lastCall[0].routes[0].score_threshold).toBe(0.9);
+ });
+ });
+
+ it("should add multiple routes", async () => {
+ const user = userEvent.setup();
+ render(
);
+
+ await user.click(screen.getByRole("button", { name: /add route/i }));
+ await user.click(screen.getByRole("button", { name: /add route/i }));
+
+ expect(screen.getByText("Route 1: Unnamed")).toBeInTheDocument();
+ expect(screen.getByText("Route 2: Unnamed")).toBeInTheDocument();
+ });
+
+ it("should toggle JSON preview visibility", async () => {
+ const user = userEvent.setup();
+ const { container } = render(
);
+
+ expect(screen.getByText("JSON Preview")).toBeInTheDocument();
+ expect(screen.getByRole("button", { name: "Show" })).toBeInTheDocument();
+ expect(container.querySelector("pre")).not.toBeInTheDocument();
+
+ await user.click(screen.getByRole("button", { name: "Show" }));
+
+ expect(screen.getByRole("button", { name: "Hide" })).toBeInTheDocument();
+ expect(container.querySelector("pre")).toBeInTheDocument();
+
+ await user.click(screen.getByRole("button", { name: "Hide" }));
+
+ expect(screen.getByRole("button", { name: "Show" })).toBeInTheDocument();
+ expect(container.querySelector("pre")).not.toBeInTheDocument();
+ });
+
+ it("should display JSON preview with route data when routes exist", async () => {
+ const user = userEvent.setup();
+ const { container } = render(
+
,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByText("Route 1: gpt-4")).toBeInTheDocument();
+ });
+
+ await user.click(screen.getByRole("button", { name: "Show" }));
+
+ const preElement = container.querySelector("pre");
+ expect(preElement).toBeInTheDocument();
+ expect(preElement?.textContent).toContain("gpt-4");
+ expect(preElement?.textContent).toContain("hello");
+ expect(preElement?.textContent).toContain("0.8");
+ });
+
+ it("should display model selector with options from modelInfo", async () => {
+ const value = {
+ routes: [
+ { name: "", utterances: [], description: "", score_threshold: 0.5 },
+ ],
+ };
+ render(
);
+
+ await waitFor(() => {
+ expect(screen.getByText("Route 1: Unnamed")).toBeInTheDocument();
+ });
+
+ expect(screen.getByText("Model")).toBeInTheDocument();
+ const comboboxes = screen.getAllByRole("combobox");
+ expect(comboboxes.length).toBeGreaterThan(0);
+ });
+
+ it("should clear routes when value prop changes to empty", async () => {
+ const value = {
+ routes: [
+ {
+ name: "gpt-4",
+ utterances: [],
+ description: "",
+ score_threshold: 0.5,
+ },
+ ],
+ };
+ const { rerender } = render(
);
+
+ await waitFor(() => {
+ expect(screen.getByText("Route 1: gpt-4")).toBeInTheDocument();
+ });
+
+ rerender(
);
+
+ await waitFor(() => {
+ expect(screen.getByText(/no routes configured/i)).toBeInTheDocument();
+ });
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/add_model/RouterConfigBuilder.tsx b/ui/litellm-dashboard/src/components/add_model/RouterConfigBuilder.tsx
new file mode 100644
index 00000000000..9b93287b36d
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/add_model/RouterConfigBuilder.tsx
@@ -0,0 +1,277 @@
+import { DeleteOutlined, InfoCircleOutlined, PlusOutlined } from "@ant-design/icons";
+import { Select as AntdSelect, Button, Card, Collapse, Divider, Empty, Flex, Input, InputNumber, Space, Tooltip, Typography } from "antd";
+import React, { useEffect, useState } from "react";
+import { ModelGroup } from "../playground/llm_calls/fetch_models";
+
+const { Text } = Typography;
+
+const { TextArea } = Input;
+
+interface Route {
+ id: string;
+ model: string;
+ utterances: string[];
+ description: string;
+ score_threshold: number;
+}
+
+interface SavedRoute {
+ id?: string;
+ name?: string;
+ model?: string;
+ utterances?: string[];
+ description?: string;
+ score_threshold?: number;
+}
+
+interface RouterConfig {
+ routes?: SavedRoute[];
+}
+
+interface RouterConfigBuilderProps {
+ modelInfo: ModelGroup[];
+ value?: RouterConfig;
+ onChange?: (config: any) => void;
+}
+
+const RouterConfigBuilder: React.FC
= ({ modelInfo, value, onChange }) => {
+ const [routes, setRoutes] = useState([]);
+ const [showJsonPreview, setShowJsonPreview] = useState(false);
+ const [expandedRoutes, setExpandedRoutes] = useState([]);
+
+ // Initialize routes from value prop - preserve existing route IDs to avoid focus loss when parent re-renders
+ useEffect(() => {
+ const routesFromValue = value?.routes;
+ if (routesFromValue) {
+ const routeIds: string[] = [];
+ setRoutes((prevRoutes) => {
+ const initializedRoutes = routesFromValue.map((route: SavedRoute, index: number) => {
+ const existingRoute = prevRoutes[index];
+ const id = existingRoute?.id || route.id || `route-${index}-${Date.now()}`;
+ routeIds.push(id);
+ return {
+ id,
+ model: route.name || route.model || "", // handle both 'name' and 'model' fields
+ utterances: route.utterances || [],
+ description: route.description || "",
+ score_threshold: route.score_threshold ?? 0.5,
+ };
+ });
+ return initializedRoutes;
+ });
+ setExpandedRoutes(routeIds);
+ } else {
+ setRoutes([]);
+ setExpandedRoutes([]);
+ }
+ }, [value]);
+
+ // Handle adding a new route
+ const addRoute = () => {
+ const newRouteId = `route-${Date.now()}`;
+ const newRoute: Route = {
+ id: newRouteId,
+ model: "",
+ utterances: [],
+ description: "",
+ score_threshold: 0.5,
+ };
+ const updatedRoutes = [...routes, newRoute];
+ setRoutes(updatedRoutes);
+ updateConfig(updatedRoutes);
+ // Automatically expand the new route
+ setExpandedRoutes((prev) => [...prev, newRouteId]);
+ };
+
+ // Handle removing a route
+ const removeRoute = (routeId: string) => {
+ const updatedRoutes = routes.filter((route) => route.id !== routeId);
+ setRoutes(updatedRoutes);
+ updateConfig(updatedRoutes);
+ // Remove from expanded routes as well
+ setExpandedRoutes((prev) => prev.filter((id) => id !== routeId));
+ };
+
+ // Handle updating a route
+ const updateRoute = (routeId: string, field: keyof Route, value: any) => {
+ const updatedRoutes = routes.map((route) => (route.id === routeId ? { ...route, [field]: value } : route));
+ setRoutes(updatedRoutes);
+ updateConfig(updatedRoutes);
+ };
+
+ // Update the overall configuration
+ const updateConfig = (updatedRoutes: Route[]) => {
+ const config = {
+ routes: updatedRoutes.map((route) => ({
+ name: route.model,
+ utterances: route.utterances,
+ description: route.description,
+ score_threshold: route.score_threshold,
+ })),
+ };
+ onChange?.(config);
+ };
+
+ // Handle utterances change (convert textarea string to array)
+ const handleUtterancesChange = (routeId: string, utterancesText: string) => {
+ const utterancesArray = utterancesText
+ .split("\n")
+ .map((line) => line.trim()) // Only trims leading/trailing whitespace, preserves internal spaces
+ .filter((line) => line.length > 0);
+ updateRoute(routeId, "utterances", utterancesArray);
+ };
+
+ // Prepare model options for dropdowns
+ const modelOptions = modelInfo.map((model) => ({
+ value: model.model_group,
+ label: model.model_group,
+ }));
+
+ const generateConfig = () => {
+ return {
+ routes: routes.map((route) => ({
+ name: route.model,
+ utterances: route.utterances,
+ description: route.description,
+ score_threshold: route.score_threshold,
+ })),
+ };
+ };
+
+ return (
+
+
+
+ Routes Configuration
+
+
+
+
+ } onClick={addRoute} className="bg-blue-600 hover:bg-blue-700">
+ Add Route
+
+
+
+ {/* Routes */}
+ {routes.length === 0 ? (
+
+
+
+ ) : (
+
setExpandedRoutes(Array.isArray(keys) ? keys : [keys].filter(Boolean))}
+ style={{ width: "100%" }}
+ items={routes.map((route, index) => ({
+ key: route.id,
+ label: (
+
+ Route {index + 1}: {route.model || "Unnamed"}
+
+ ),
+ extra: (
+ }
+ onClick={(e) => {
+ e.stopPropagation();
+ removeRoute(route.id);
+ }}
+ />
+ ),
+ children: (
+
+ {/* Model Selection */}
+
+
Model
+
updateRoute(route.id, "model", value)}
+ placeholder="Select model"
+ showSearch
+ style={{ width: "100%" }}
+ options={modelOptions}
+ />
+
+
+ {/* Description */}
+
+ Description
+
+
+ {/* Score Threshold */}
+
+
+ Score Threshold
+
+
+
+
+
updateRoute(route.id, "score_threshold", value || 0)}
+ min={0}
+ max={1}
+ step={0.1}
+ style={{ width: "100%" }}
+ placeholder="0.5"
+ />
+
+
+ {/* Example Utterances */}
+
+
+ Example Utterances
+
+
+
+
+
+ Type an utterance and press Enter to add it. You can also paste multiple lines.
+
+
updateRoute(route.id, "utterances", utterances)}
+ placeholder="Type an utterance and press Enter..."
+ style={{ width: "100%" }}
+ tokenSeparators={["\n"]}
+ maxTagCount="responsive"
+ allowClear
+ />
+
+
+ ),
+ }))}
+ />
+ )}
+
+ {/* JSON Preview */}
+
+
+ JSON Preview
+ setShowJsonPreview(!showJsonPreview)} className="text-blue-600 p-0">
+ {showJsonPreview ? "Hide" : "Show"}
+
+
+
+ {showJsonPreview && (
+
+ {JSON.stringify(generateConfig(), null, 2)}
+
+ )}
+
+
+ );
+};
+
+export default RouterConfigBuilder;
diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx
index 6fff07cb96f..fb68d6f59e3 100644
--- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx
+++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx
@@ -7,7 +7,7 @@ import ConnectionErrorDisplay from "./model_connection_test";
import { all_admin_roles } from "@/utils/roles";
import { handleAddAutoRouterSubmit } from "./handle_add_auto_router_submit";
import { fetchAvailableModels, ModelGroup } from "../playground/llm_calls/fetch_models";
-import RouterConfigBuilder from "./router_config_builder";
+import RouterConfigBuilder from "./RouterConfigBuilder";
import NotificationManager from "../molecules/notifications_manager";
interface AddAutoRouterTabProps {
diff --git a/ui/litellm-dashboard/src/components/add_model/router_config_builder.tsx b/ui/litellm-dashboard/src/components/add_model/router_config_builder.tsx
deleted file mode 100644
index b6e8e8758c2..00000000000
--- a/ui/litellm-dashboard/src/components/add_model/router_config_builder.tsx
+++ /dev/null
@@ -1,281 +0,0 @@
-import React, { useState, useEffect } from "react";
-import { Card, Button, Input, InputNumber, Select as AntdSelect, Tooltip, Collapse } from "antd";
-import { PlusOutlined, DeleteOutlined, InfoCircleOutlined, DownOutlined } from "@ant-design/icons";
-import { Text } from "@tremor/react";
-import { ModelGroup } from "../playground/llm_calls/fetch_models";
-
-const { TextArea } = Input;
-const { Panel } = Collapse;
-
-interface Route {
- id: string;
- model: string;
- utterances: string[];
- description: string;
- score_threshold: number;
-}
-
-interface SavedRoute {
- id?: string;
- name?: string;
- model?: string;
- utterances?: string[];
- description?: string;
- score_threshold?: number;
-}
-
-interface RouterConfig {
- routes?: SavedRoute[];
-}
-
-interface RouterConfigBuilderProps {
- modelInfo: ModelGroup[];
- value?: RouterConfig;
- onChange?: (config: any) => void;
-}
-
-const RouterConfigBuilder: React.FC = ({ modelInfo, value, onChange }) => {
- const [routes, setRoutes] = useState([]);
- const [showJsonPreview, setShowJsonPreview] = useState(false);
- const [expandedRoutes, setExpandedRoutes] = useState([]);
-
- // Initialize routes from value prop
- useEffect(() => {
- if (value?.routes) {
- const initializedRoutes = value.routes.map((route: SavedRoute, index: number) => ({
- id: route.id || `route-${index}-${Date.now()}`,
- model: route.name || route.model || "", // handle both 'name' and 'model' fields
- utterances: route.utterances || [],
- description: route.description || "",
- score_threshold: route.score_threshold || 0.5,
- }));
- setRoutes(initializedRoutes);
-
- // Set expanded routes for existing routes
- const routeIds = initializedRoutes.map((route) => route.id);
- setExpandedRoutes(routeIds);
- } else {
- setRoutes([]);
- setExpandedRoutes([]);
- }
- }, [value]);
-
- // Handle adding a new route
- const addRoute = () => {
- const newRouteId = `route-${Date.now()}`;
- const newRoute: Route = {
- id: newRouteId,
- model: "",
- utterances: [],
- description: "",
- score_threshold: 0.5,
- };
- const updatedRoutes = [...routes, newRoute];
- setRoutes(updatedRoutes);
- updateConfig(updatedRoutes);
- // Automatically expand the new route
- setExpandedRoutes((prev) => [...prev, newRouteId]);
- };
-
- // Handle removing a route
- const removeRoute = (routeId: string) => {
- const updatedRoutes = routes.filter((route) => route.id !== routeId);
- setRoutes(updatedRoutes);
- updateConfig(updatedRoutes);
- // Remove from expanded routes as well
- setExpandedRoutes((prev) => prev.filter((id) => id !== routeId));
- };
-
- // Handle updating a route
- const updateRoute = (routeId: string, field: keyof Route, value: any) => {
- const updatedRoutes = routes.map((route) => (route.id === routeId ? { ...route, [field]: value } : route));
- setRoutes(updatedRoutes);
- updateConfig(updatedRoutes);
- };
-
- // Update the overall configuration
- const updateConfig = (updatedRoutes: Route[]) => {
- const config = {
- routes: updatedRoutes.map((route) => ({
- name: route.model,
- utterances: route.utterances,
- description: route.description,
- score_threshold: route.score_threshold,
- })),
- };
- onChange?.(config);
- };
-
- // Handle utterances change (convert textarea string to array)
- const handleUtterancesChange = (routeId: string, utterancesText: string) => {
- const utterancesArray = utterancesText
- .split("\n")
- .map((line) => line.trim()) // Only trims leading/trailing whitespace, preserves internal spaces
- .filter((line) => line.length > 0);
- updateRoute(routeId, "utterances", utterancesArray);
- };
-
- // Prepare model options for dropdowns
- const modelOptions = modelInfo.map((model) => ({
- value: model.model_group,
- label: model.model_group,
- }));
-
- const generateConfig = () => {
- return {
- routes: routes.map((route) => ({
- name: route.model,
- utterances: route.utterances,
- description: route.description,
- score_threshold: route.score_threshold,
- })),
- };
- };
-
- return (
-
- {/* Routes Configuration Header */}
-
-
- Routes Configuration
-
-
-
-
-
} onClick={addRoute} className="bg-blue-600 hover:bg-blue-700">
- Add Route
-
-
-
- {/* Routes */}
- {routes.length === 0 ? (
-
- No routes configured. Click “Add Route” to get started.
-
- ) : (
-
- {routes.map((route, index) => (
-
- }
- activeKey={expandedRoutes}
- onChange={(keys) => setExpandedRoutes(Array.isArray(keys) ? keys : [keys].filter(Boolean))}
- items={[
- {
- key: route.id,
- label: (
-
-
- Route {index + 1}: {route.model || "Unnamed"}
-
- }
- onClick={(e) => {
- e.stopPropagation();
- removeRoute(route.id);
- }}
- className="mr-2"
- />
-
- ),
- children: (
-
- {/* Model Selection */}
-
-
Model
-
updateRoute(route.id, "model", value)}
- placeholder="Select model"
- showSearch
- style={{ width: "100%" }}
- options={modelOptions}
- />
-
-
- {/* Description */}
-
- Description
-
-
- {/* Score Threshold */}
-
-
- Score Threshold
-
-
-
-
-
updateRoute(route.id, "score_threshold", value || 0)}
- min={0}
- max={1}
- step={0.1}
- style={{ width: "100%" }}
- placeholder="0.5"
- />
-
-
- {/* Example Utterances */}
-
-
- Example Utterances
-
-
-
-
-
- Type an utterance and press Enter to add it. You can also paste multiple lines.
-
-
updateRoute(route.id, "utterances", utterances)}
- placeholder="Type an utterance and press Enter..."
- style={{ width: "100%" }}
- tokenSeparators={["\n"]}
- maxTagCount="responsive"
- allowClear
- />
-
-
- ),
- },
- ]}
- />
-
- ))}
-
- )}
-
- {/* JSON Preview */}
-
-
- JSON Preview
- setShowJsonPreview(!showJsonPreview)} className="text-blue-600 p-0">
- {showJsonPreview ? "Hide" : "Show"}
-
-
-
- {showJsonPreview && (
-
- {JSON.stringify(generateConfig(), null, 2)}
-
- )}
-
-
- );
-};
-
-export default RouterConfigBuilder;
diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx
index 1ed1d3102fe..e4e6ebd1da9 100644
--- a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx
+++ b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx
@@ -3,7 +3,7 @@ import { Modal, Form, Button, Select as AntdSelect } from "antd";
import { Text, TextInput } from "@tremor/react";
import { modelAvailableCall, modelPatchUpdateCall } from "../networking";
import { fetchAvailableModels, ModelGroup } from "../playground/llm_calls/fetch_models";
-import RouterConfigBuilder from "../add_model/router_config_builder";
+import RouterConfigBuilder from "../add_model/RouterConfigBuilder";
import NotificationsManager from "../molecules/notifications_manager";
interface EditAutoRouterModalProps {
diff --git a/ui/litellm-dashboard/src/components/guardrails/add_guardrail_form.tsx b/ui/litellm-dashboard/src/components/guardrails/add_guardrail_form.tsx
index f4bfa304811..71b61904dd5 100644
--- a/ui/litellm-dashboard/src/components/guardrails/add_guardrail_form.tsx
+++ b/ui/litellm-dashboard/src/components/guardrails/add_guardrail_form.tsx
@@ -1,21 +1,20 @@
-import React, { useState, useEffect, useMemo } from "react";
-import { Form, Typography, Select, Modal, Tag, Steps } from "antd";
-import { Button, TextInput } from "@tremor/react";
-import {
- guardrail_provider_map,
- shouldRenderPIIConfigSettings,
- shouldRenderContentFilterConfigSettings,
- guardrailLogoMap,
- populateGuardrailProviders,
- populateGuardrailProviderMap,
- getGuardrailProviders,
-} from "./guardrail_info_helpers";
-import { createGuardrailCall, getGuardrailUISettings, getGuardrailProviderSpecificParams } from "../networking";
-import PiiConfiguration from "./pii_configuration";
-import GuardrailProviderFields from "./guardrail_provider_fields";
-import GuardrailOptionalParams from "./guardrail_optional_params";
+import { Button, Form, Input, Modal, Select, Steps, Tag, Typography } from "antd";
+import React, { useEffect, useMemo, useState } from "react";
import NotificationsManager from "../molecules/notifications_manager";
+import { createGuardrailCall, getGuardrailProviderSpecificParams, getGuardrailUISettings } from "../networking";
import ContentFilterConfiguration from "./content_filter/ContentFilterConfiguration";
+import {
+ getGuardrailProviders,
+ guardrail_provider_map,
+ guardrailLogoMap,
+ populateGuardrailProviderMap,
+ populateGuardrailProviders,
+ shouldRenderContentFilterConfigSettings,
+ shouldRenderPIIConfigSettings,
+} from "./guardrail_info_helpers";
+import GuardrailOptionalParams from "./guardrail_optional_params";
+import GuardrailProviderFields from "./guardrail_provider_fields";
+import PiiConfiguration from "./pii_configuration";
import ToolPermissionRulesEditor, {
ToolPermissionConfig,
} from "./tool_permission/ToolPermissionRulesEditor";
@@ -457,7 +456,7 @@ const AddGuardrailForm: React.FC = ({ visible, onClose, a
label="Guardrail Name"
rules={[{ required: true, message: "Please enter a guardrail name" }]}
>
-
+
= ({ visible, onClose, a
- >
- )}
+
+ >
+ )}
@@ -721,21 +720,21 @@ const AddGuardrailForm: React.FC