mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix(ui): restrict heuristic v2 to proxy admins
This commit is contained in:
parent
0f85c3e640
commit
56f009400f
4 changed files with 49 additions and 6 deletions
|
|
@ -159,15 +159,21 @@ interface ClassificationMethodConfigProps {
|
|||
showValidationErrors?: boolean;
|
||||
/** The resolved default model - see resolveComplexityDefaultModel. Names and gates the radio. */
|
||||
defaultModel?: string;
|
||||
/** Mirrors the backend's proxy-admin-only heuristic_v2 write gate. */
|
||||
allowHeuristicV2?: boolean;
|
||||
}
|
||||
|
||||
const ClassifierTypeRadios: React.FC<{
|
||||
value: ComplexityRouterConfigValue;
|
||||
classifierType: ClassifierType;
|
||||
onTypeChange: (classifierType: ClassifierType) => void;
|
||||
}> = ({ value, classifierType, onTypeChange }) => {
|
||||
allowHeuristicV2: boolean;
|
||||
}> = ({ value, classifierType, onTypeChange, allowHeuristicV2 }) => {
|
||||
const scorerLocked = Boolean(value.custom_tier_set);
|
||||
const scorerLockedReason = restrictedBy(value, "heuristicClassifier")?.reason;
|
||||
const heuristicV2Locked = scorerLocked || !allowHeuristicV2;
|
||||
const heuristicV2LockedReason =
|
||||
scorerLockedReason ?? (!allowHeuristicV2 ? "Only proxy admins can configure Heuristic v2." : undefined);
|
||||
return (
|
||||
<RadioGroup
|
||||
value={classifierType}
|
||||
|
|
@ -186,9 +192,9 @@ const ClassifierTypeRadios: React.FC<{
|
|||
</span>
|
||||
</Label>
|
||||
</SimpleTooltip>
|
||||
<SimpleTooltip content={scorerLockedReason}>
|
||||
<SimpleTooltip content={heuristicV2LockedReason}>
|
||||
<Label className="items-start font-normal leading-normal has-data-disabled:cursor-not-allowed has-data-disabled:opacity-50">
|
||||
<RadioGroupItem value="heuristic_v2" className="mt-0.5" disabled={scorerLocked} />
|
||||
<RadioGroupItem value="heuristic_v2" className="mt-0.5" disabled={heuristicV2Locked} />
|
||||
<span>
|
||||
<strong className="font-semibold">Heuristic v2</strong>{" "}
|
||||
<span className="text-muted-foreground">
|
||||
|
|
@ -240,6 +246,7 @@ const ClassificationMethodConfig: React.FC<ClassificationMethodConfigProps> = ({
|
|||
onCustomTechnicalKeywordsChange,
|
||||
showValidationErrors = false,
|
||||
defaultModel,
|
||||
allowHeuristicV2 = false,
|
||||
}) => {
|
||||
const [draft, setDraft] = React.useState<{ id: string; raw: string } | null>(null);
|
||||
const hasDefaultModel = Boolean(defaultModel);
|
||||
|
|
@ -387,7 +394,12 @@ const ClassificationMethodConfig: React.FC<ClassificationMethodConfigProps> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<ClassifierTypeRadios value={value} classifierType={classifierType} onTypeChange={handleClassifierTypeChange} />
|
||||
<ClassifierTypeRadios
|
||||
value={value}
|
||||
classifierType={classifierType}
|
||||
onTypeChange={handleClassifierTypeChange}
|
||||
allowHeuristicV2={allowHeuristicV2}
|
||||
/>
|
||||
|
||||
{classifierType === "heuristic_first" && (
|
||||
<div className="mt-4 space-y-2">
|
||||
|
|
|
|||
|
|
@ -130,7 +130,12 @@ describe("ComplexityRouterConfig", () => {
|
|||
it("selects heuristic v2 without requiring a classifier model or showing weighted scoring", () => {
|
||||
const onChange = vi.fn();
|
||||
const { rerender } = renderWithProviders(
|
||||
<ComplexityRouterConfig modelInfo={mockModelInfo} value={defaultValue} onChange={onChange} />,
|
||||
<ComplexityRouterConfig
|
||||
modelInfo={mockModelInfo}
|
||||
value={defaultValue}
|
||||
onChange={onChange}
|
||||
allowHeuristicV2={true}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText("Advanced: Classification Method"));
|
||||
|
|
@ -144,7 +149,14 @@ describe("ComplexityRouterConfig", () => {
|
|||
);
|
||||
|
||||
const heuristicV2Value: ComplexityRouterConfigValue = { ...defaultValue, classifier_type: "heuristic_v2" };
|
||||
rerender(<ComplexityRouterConfig modelInfo={mockModelInfo} value={heuristicV2Value} onChange={onChange} />);
|
||||
rerender(
|
||||
<ComplexityRouterConfig
|
||||
modelInfo={mockModelInfo}
|
||||
value={heuristicV2Value}
|
||||
onChange={onChange}
|
||||
allowHeuristicV2={true}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText("Classifier Model")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Advanced scoring")).not.toBeInTheDocument();
|
||||
|
|
@ -152,6 +164,21 @@ describe("ComplexityRouterConfig", () => {
|
|||
expect(screen.queryByText(/Score < 0.15/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("reserves heuristic v2 selection for proxy admins", () => {
|
||||
const onChange = vi.fn();
|
||||
renderWithProviders(<ComplexityRouterConfig {...baseProps} onChange={onChange} allowHeuristicV2={false} />);
|
||||
|
||||
fireEvent.click(screen.getByText("Advanced: Classification Method"));
|
||||
|
||||
const heuristicV2 = screen.getByRole("radio", { name: /Heuristic v2/ });
|
||||
expect(heuristicV2).toHaveAttribute("aria-disabled", "true");
|
||||
expect(screen.getByRole("radio", { name: /^Heuristic \(/ })).not.toHaveAttribute("aria-disabled", "true");
|
||||
expect(screen.getByRole("radio", { name: /LLM Classifier/ })).not.toHaveAttribute("aria-disabled", "true");
|
||||
|
||||
fireEvent.click(heuristicV2);
|
||||
expect(onChange).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should show classifier fields and use the configured values when classifier_type is llm", () => {
|
||||
const llmValue: ComplexityRouterConfigValue = {
|
||||
...defaultValue,
|
||||
|
|
|
|||
|
|
@ -484,6 +484,7 @@ interface ComplexityRouterConfigProps {
|
|||
escalationKeywords?: string[];
|
||||
onEscalationKeywordsChange?: (keywords: string[]) => void;
|
||||
showValidationErrors?: boolean;
|
||||
allowHeuristicV2?: boolean;
|
||||
}
|
||||
|
||||
export const TIER_DESCRIPTIONS: Record<
|
||||
|
|
@ -605,6 +606,7 @@ const ComplexityRouterConfig: React.FC<ComplexityRouterConfigProps> = ({
|
|||
escalationKeywords = [],
|
||||
onEscalationKeywordsChange,
|
||||
showValidationErrors = false,
|
||||
allowHeuristicV2 = false,
|
||||
}) => {
|
||||
const customTierSet = value.custom_tier_set;
|
||||
const tierRows = activeTierRows(value);
|
||||
|
|
@ -821,6 +823,7 @@ const ComplexityRouterConfig: React.FC<ComplexityRouterConfigProps> = ({
|
|||
onCustomTechnicalKeywordsChange={onCustomTechnicalKeywordsChange}
|
||||
showValidationErrors={showValidationErrors}
|
||||
defaultModel={defaultModel}
|
||||
allowHeuristicV2={allowHeuristicV2}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -615,6 +615,7 @@ const AddAutoRouterTab: React.FC<AddAutoRouterTabProps> = ({
|
|||
escalationKeywords={escalationKeywords}
|
||||
onEscalationKeywordsChange={setEscalationKeywords}
|
||||
showValidationErrors={showValidationErrors}
|
||||
allowHeuristicV2={createScope === "unscoped-ok"}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue