diff --git a/ui/litellm-dashboard/src/components/policies/template_parameter_modal.tsx b/ui/litellm-dashboard/src/components/policies/template_parameter_modal.tsx index 67948a075cd..379270aba82 100644 --- a/ui/litellm-dashboard/src/components/policies/template_parameter_modal.tsx +++ b/ui/litellm-dashboard/src/components/policies/template_parameter_modal.tsx @@ -39,6 +39,9 @@ const TemplateParameterModal: React.FC = ({ const [competitorTags, setCompetitorTags] = useState([]); const [variationsMap, setVariationsMap] = useState>({}); const [isGenerating, setIsGenerating] = useState(false); + const [refinementInput, setRefinementInput] = useState(""); + const [isRefining, setIsRefining] = useState(false); + const [hasGenerated, setHasGenerated] = useState(false); const parameters: TemplateParameter[] = template?.parameters || []; const hasEnrichment = !!template?.llm_enrichment; @@ -60,6 +63,9 @@ const TemplateParameterModal: React.FC = ({ setCompetitorTags([]); setVariationsMap({}); setIsGenerating(false); + setRefinementInput(""); + setIsRefining(false); + setHasGenerated(false); } }, [visible, template]); @@ -108,6 +114,7 @@ const TemplateParameterModal: React.FC = ({ setCompetitorTags(result.competitors); setVariationsMap(result.competitor_variations || {}); setIsGenerating(false); + setHasGenerated(true); }, (error) => { console.error("Streaming error:", error); @@ -120,6 +127,43 @@ const TemplateParameterModal: React.FC = ({ } }; + const handleRefine = async () => { + if (!accessToken || !selectedModel || !template || !refinementInput.trim()) return; + + setIsRefining(true); + try { + await enrichPolicyTemplateStream( + accessToken, + template.id, + parameterValues, + selectedModel, + (name) => { + setCompetitorTags((prev) => { + if (prev.some((t) => t.toLowerCase() === name.toLowerCase())) return prev; + return [...prev, name]; + }); + }, + (result) => { + setCompetitorTags(result.competitors); + setVariationsMap(result.competitor_variations || {}); + setIsRefining(false); + setRefinementInput(""); + }, + (error) => { + console.error("Refinement error:", error); + setIsRefining(false); + }, + { + instruction: refinementInput.trim(), + existingCompetitors: competitorTags, + } + ); + } catch (error) { + console.error("Error refining competitor names:", error); + setIsRefining(false); + } + }; + const allNonEnrichmentFilled = nonEnrichmentParams .filter((p) => p.required) .every((p) => (parameterValues[p.name] || "").trim().length > 0); @@ -284,6 +328,39 @@ const TemplateParameterModal: React.FC = ({

)} + + {/* Refinement input — shown after initial generation in AI mode */} + {competitorMode === "ai" && hasGenerated && competitorTags.length > 0 && ( +
+ +
+ setRefinementInput(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter" && refinementInput.trim() && !isRefining) { + handleRefine(); + } + }} + disabled={isRefining} + /> + +
+

+ Give instructions to add, remove, or change competitors. Press Enter to send. +

+
+ )} )}