mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-29 01:42:19 +00:00
fix(ui): adapt the jev dashboard pieces to stable/1.102.x
This commit is contained in:
parent
d0687347a7
commit
4bfac88281
9 changed files with 24 additions and 104 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import { transitionClassifierType } from "./classifier_type_transition";
|
||||
import JevClassifierConfig from "./JevClassifierConfig";
|
||||
import { Info } from "lucide-react";
|
||||
import { SimpleTooltip } from "@/components/ui/tooltip";
|
||||
|
|
@ -18,7 +19,6 @@ import ClassifierReasoningEffortSelect from "./ClassifierReasoningEffortSelect";
|
|||
import ClassifierCircuitBreakerConfig from "./ClassifierCircuitBreakerConfig";
|
||||
import ClassifierVisionConfig from "./ClassifierVisionConfig";
|
||||
import type { ReasoningEffort } from "./complexity_router_tiers";
|
||||
import { nonReasoningTierFields } from "./nonReasoningTierFields";
|
||||
import { useComplexityScorerDefaults } from "@/app/(dashboard)/hooks/autoRouter/useComplexityScorerDefaults";
|
||||
import {
|
||||
ClassificationFrequency,
|
||||
|
|
@ -34,12 +34,10 @@ import {
|
|||
DEFAULT_CLASSIFIER_FALLBACK,
|
||||
DEFAULT_CLASSIFIER_TIMEOUT_MS,
|
||||
DEFAULT_CLASSIFICATION_RUBRIC,
|
||||
NEW_CLASSIFIER_CLASSIFICATION_RUBRIC,
|
||||
ClassificationRubric,
|
||||
effectiveTierLabel,
|
||||
heuristicScoringRole,
|
||||
usesLlmClassifier,
|
||||
DEFAULT_HEURISTIC_FIRST_MAX_TIER,
|
||||
usesClassifierContext,
|
||||
DEFAULT_HYBRID_BOUNDARY_MARGIN,
|
||||
HEURISTIC_FIRST_MAX_TIER_KEYS,
|
||||
|
|
@ -272,35 +270,7 @@ const ClassificationMethodConfig: React.FC<ClassificationMethodConfigProps> = ({
|
|||
const explicitlySupportedClassifierEfforts = effortOptionsByModel[classifierModel];
|
||||
|
||||
const handleClassifierTypeChange = (classifierType: ClassifierType) => {
|
||||
const nextValue: ComplexityRouterConfigValue = {
|
||||
...value,
|
||||
classifier_type: classifierType,
|
||||
classifier_llm_config: usesLlmClassifier(classifierType)
|
||||
? value.classifier_llm_config ?? {
|
||||
model: "",
|
||||
timeout_ms: DEFAULT_CLASSIFIER_TIMEOUT_MS,
|
||||
classification_rubric: NEW_CLASSIFIER_CLASSIFICATION_RUBRIC,
|
||||
}
|
||||
: undefined,
|
||||
classifier_context_window_size: usesLlmClassifier(classifierType)
|
||||
? value.classifier_context_window_size ?? DEFAULT_CLASSIFIER_CONTEXT_WINDOW_SIZE
|
||||
: undefined,
|
||||
classifier_context_budget_chars: usesLlmClassifier(classifierType)
|
||||
? value.classifier_context_budget_chars ?? DEFAULT_CLASSIFIER_CONTEXT_BUDGET_CHARS
|
||||
: undefined,
|
||||
classifier_context_include_assistant_turns: usesLlmClassifier(classifierType)
|
||||
? value.classifier_context_include_assistant_turns
|
||||
: undefined,
|
||||
classifier_fallback: usesLlmClassifier(classifierType) ? value.classifier_fallback : undefined,
|
||||
heuristic_first_max_tier:
|
||||
classifierType === "heuristic_first"
|
||||
? value.heuristic_first_max_tier ?? DEFAULT_HEURISTIC_FIRST_MAX_TIER
|
||||
: undefined,
|
||||
hybrid_boundary_margin:
|
||||
classifierType === "hybrid" ? value.hybrid_boundary_margin ?? DEFAULT_HYBRID_BOUNDARY_MARGIN : undefined,
|
||||
...nonReasoningTierFields(classifierType, value),
|
||||
};
|
||||
onChange(nextValue);
|
||||
onChange(transitionClassifierType(value, classifierType));
|
||||
};
|
||||
|
||||
const handleHeuristicFirstMaxTierChange = (tier: string) => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
|||
import { fireEvent, renderWithProviders, screen } from "../../../tests/test-utils";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import ClassificationMethodConfig from "./ClassificationMethodConfig";
|
||||
import AutoRouterClassifierTabs from "./AutoRouterClassifierTabs";
|
||||
import JevEditor from "./JevClassifierConfig";
|
||||
import { type ComplexityRouterConfigValue } from "./ComplexityRouterConfig";
|
||||
import {
|
||||
|
|
@ -50,7 +49,7 @@ const initial: ComplexityRouterConfigValue = {
|
|||
function Form() {
|
||||
const [value, setValue] = useState(initial);
|
||||
return (
|
||||
<AutoRouterClassifierTabs value={value} onChange={setValue}>
|
||||
<>
|
||||
<ClassificationMethodConfig
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
|
|
@ -90,7 +89,7 @@ function Form() {
|
|||
>
|
||||
Probe current config
|
||||
</button>
|
||||
</AutoRouterClassifierTabs>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +102,6 @@ describe("JEV classifier editor", () => {
|
|||
expect(screen.getByText("Classifier Prompt")).toBeInTheDocument();
|
||||
expect(screen.getByRole("switch", { name: "Use images for classification" })).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole("radio", { name: /JEV Classifier/ }));
|
||||
expect(screen.getByRole("tab", { name: "Complexity" })).toHaveAttribute("aria-selected", "true");
|
||||
expect(screen.getByLabelText("JEV Model")).toHaveValue("jev-latest");
|
||||
expect(screen.getByLabelText("JEV Instructions")).toBeDisabled();
|
||||
expect(screen.queryByLabelText("Classifier Model")).not.toBeInTheDocument();
|
||||
|
|
|
|||
|
|
@ -535,6 +535,17 @@ const AddAutoRouterTab: React.FC<AddAutoRouterTabProps> = ({
|
|||
setIsTestModalVisible(true);
|
||||
};
|
||||
|
||||
const jevConnectionTestParams =
|
||||
effectiveClassifierType(complexityRouterConfig) === "jev"
|
||||
? {
|
||||
prompt: JEV_CONNECTION_TEST_PROMPT,
|
||||
config: buildComplexityRouterConfig(complexityRouterConfigParams),
|
||||
defaultModel: resolveComplexityDefaultModel(complexityRouterConfig, complexityRouterConfig.default_model),
|
||||
routerName: watchedName,
|
||||
teamId: requiresTeamScope ? watchedTeamId ?? undefined : undefined,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Card>
|
||||
|
|
@ -804,20 +815,7 @@ const AddAutoRouterTab: React.FC<AddAutoRouterTabProps> = ({
|
|||
testId={connectionTestId}
|
||||
accessToken={accessToken}
|
||||
targets={testTargets}
|
||||
jevRequest={
|
||||
effectiveClassifierType(complexityRouterConfig) === "jev"
|
||||
? buildAutoRouterRoutingTestRequest({
|
||||
prompt: JEV_CONNECTION_TEST_PROMPT,
|
||||
config: buildComplexityRouterConfig(complexityRouterConfigParams),
|
||||
defaultModel: resolveComplexityDefaultModel(
|
||||
complexityRouterConfig,
|
||||
complexityRouterConfig.default_model,
|
||||
),
|
||||
routerName: watchedName,
|
||||
teamId: requiresTeamScope ? watchedTeamId ?? undefined : undefined,
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
jevRequest={jevConnectionTestParams && buildAutoRouterRoutingTestRequest(jevConnectionTestParams)}
|
||||
onTestComplete={() => setIsTestingConnection(false)}
|
||||
/>
|
||||
</TooltipProvider>
|
||||
|
|
|
|||
|
|
@ -495,7 +495,7 @@ const classifierWireFields = (
|
|||
| "classifierContextIncludeAssistantTurns"
|
||||
>,
|
||||
): Partial<ComplexityRouterConfigPayload> => ({
|
||||
...(usesClassifierContext(effectiveType) &&
|
||||
...(usesLlmClassifier(effectiveType) &&
|
||||
classifierLlmConfig && { classifier_llm_config: normalizeClassifierLlmConfig(classifierLlmConfig) }),
|
||||
...(usesClassifierContext(effectiveType) &&
|
||||
classifierFallback !== undefined && { classifier_fallback: classifierFallback }),
|
||||
|
|
|
|||
|
|
@ -66,39 +66,6 @@ describe("transitionClassifierType", () => {
|
|||
expect(result).toMatchObject(expectedSettings);
|
||||
});
|
||||
|
||||
it.each(["capability", "llm_v2"] as const)("requires explicit policy input for a new %s classifier", (target) => {
|
||||
const result = transitionClassifierType(standard, target);
|
||||
expect(result.classifier_llm_config).toEqual({ model: "judge", timeout_ms: 20000 });
|
||||
expect(result.classifier_fallback).toBeUndefined();
|
||||
if (target === "capability") {
|
||||
expect(result.capability_classifier_config?.base_threshold).toBeNaN();
|
||||
} else {
|
||||
expect(result.llm_v2_config).toMatchObject({ efficient_profile: "", capable_profile: "", harness: "" });
|
||||
expect(result.llm_v2_config?.max_quality_gap).toBeNaN();
|
||||
}
|
||||
expect(standard.tiers.MEDIUM).toEqual(["middle"]);
|
||||
expect(standard.classifier_llm_config?.classification_rubric).toBe("business");
|
||||
});
|
||||
|
||||
it.each([
|
||||
["capability", "llm"],
|
||||
["capability", "heuristic_first"],
|
||||
["capability", "hybrid"],
|
||||
["llm_v2", "llm"],
|
||||
["llm_v2", "heuristic_first"],
|
||||
["llm_v2", "hybrid"],
|
||||
] as const)("restores the complexity rubric from %s to %s while preserving the judge", (source, target) => {
|
||||
const forecast = transitionClassifierType(standard, source);
|
||||
const result = transitionClassifierType(forecast, target);
|
||||
expect(result.classifier_llm_config).toEqual({
|
||||
model: "judge",
|
||||
timeout_ms: 20000,
|
||||
classification_rubric: "agentic",
|
||||
});
|
||||
expect(result.capability_classifier_config).toBeUndefined();
|
||||
expect(result.llm_v2_config).toBeUndefined();
|
||||
});
|
||||
|
||||
it("clears the inactive non-reasoning pool and plan floor when switching to local classification", () => {
|
||||
const initial: ComplexityRouterConfigValue = {
|
||||
...standard,
|
||||
|
|
|
|||
|
|
@ -11,19 +11,17 @@ import {
|
|||
usesClassifierContext,
|
||||
} from "./ComplexityRouterConfig";
|
||||
import { defaultJevClassifierConfig } from "./jev_classifier_config";
|
||||
import { isForecastClassifier, prepareForecastClassifier } from "./forecast_classifier_config";
|
||||
import { nonReasoningTierFields } from "./nonReasoningTierFields";
|
||||
|
||||
export const transitionClassifierType = (
|
||||
value: ComplexityRouterConfigValue,
|
||||
classifierType: ClassifierType,
|
||||
): ComplexityRouterConfigValue => {
|
||||
const startsLlmRubric =
|
||||
!value.classifier_llm_config ||
|
||||
(isForecastClassifier(value.classifier_type) && !isForecastClassifier(classifierType));
|
||||
const startsLlmRubric = !value.classifier_llm_config;
|
||||
const judgeConfig = value.classifier_llm_config ?? { model: "", timeout_ms: DEFAULT_CLASSIFIER_TIMEOUT_MS };
|
||||
const nextValue: ComplexityRouterConfigValue = {
|
||||
...value,
|
||||
classifier_type: classifierType,
|
||||
jev_classifier_config:
|
||||
classifierType === "jev" ? value.jev_classifier_config ?? defaultJevClassifierConfig() : undefined,
|
||||
classification_prompt: classifierType === "jev" ? undefined : value.classification_prompt,
|
||||
|
|
@ -55,5 +53,5 @@ export const transitionClassifierType = (
|
|||
classifierType === "hybrid" ? value.hybrid_boundary_margin ?? DEFAULT_HYBRID_BOUNDARY_MARGIN : undefined,
|
||||
...nonReasoningTierFields(classifierType, value),
|
||||
};
|
||||
return prepareForecastClassifier(nextValue, classifierType);
|
||||
return nextValue;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,15 +1,7 @@
|
|||
export type ClassifierType =
|
||||
| "heuristic"
|
||||
| "heuristic_v2"
|
||||
| "llm"
|
||||
| "jev"
|
||||
| "heuristic_first"
|
||||
| "hybrid"
|
||||
| "capability"
|
||||
| "llm_v2";
|
||||
export type ClassifierType = "heuristic" | "heuristic_v2" | "llm" | "jev" | "heuristic_first" | "hybrid";
|
||||
|
||||
export const usesLlmClassifier = (classifierType: ClassifierType): boolean =>
|
||||
(["llm", "heuristic_first", "hybrid", "capability", "llm_v2"] as const).some((type) => type === classifierType);
|
||||
(["llm", "heuristic_first", "hybrid"] as const).some((type) => type === classifierType);
|
||||
|
||||
export const usesClassifierContext = (classifierType: ClassifierType): boolean =>
|
||||
classifierType === "jev" || usesLlmClassifier(classifierType);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ import ComplexityRouterConfig, {
|
|||
ClassifierLLMConfig,
|
||||
ClassifierType,
|
||||
ComplexityRouterConfigValue,
|
||||
ComplexityTiers,
|
||||
effectiveClassifierType,
|
||||
heuristicScoringRole,
|
||||
DEFAULT_ADAPTIVE_WEIGHTS,
|
||||
DEFAULT_SESSION_AFFINITY,
|
||||
|
|
@ -166,7 +166,6 @@ export const hydrateComplexityRouterConfig = (
|
|||
plan_mode_min_tier: hydratePlanModeMinTier(parsedConfig.plan_mode_min_tier, custom_tier_set),
|
||||
tier_labels: hydrateTierLabels(parsedConfig.tier_labels),
|
||||
classifier_type: parsedConfig.classifier_type || "heuristic",
|
||||
classifier_llm_config: parsedConfig.classifier_llm_config,
|
||||
classifier_llm_config: parsedConfig.classifier_type === "jev" ? undefined : parsedConfig.classifier_llm_config,
|
||||
jev_classifier_config:
|
||||
parsedConfig.classifier_type === "jev"
|
||||
|
|
@ -352,9 +351,8 @@ export const buildUpdatedComplexityRouterConfig = (
|
|||
keywordMatching?: KeywordMatchingState,
|
||||
): Record<string, unknown> => {
|
||||
const isManaged = (key: string): boolean => {
|
||||
if (key === "classifier_context_per_turn_chars") {
|
||||
if (key === "classifier_context_per_turn_chars")
|
||||
return !usesClassifierContext(effectiveClassifierType(value)) || Object.prototype.hasOwnProperty.call(value, key);
|
||||
}
|
||||
if (MANAGED_COMPLEXITY_ROUTER_KEYS.has(key)) return true;
|
||||
if (keywordMatching !== undefined && KEYWORD_MATCHING_KEYS.has(key)) return true;
|
||||
return customTechnicalKeywords !== undefined && key === "custom_technical_keywords";
|
||||
|
|
|
|||
|
|
@ -284,7 +284,6 @@ export const buildPresetPrefill = (
|
|||
tier_model_params: resolveParamKeys(hydrateTierModelParams(config.tiers, config.tier_model_configs)),
|
||||
tier_labels: hydrateTierLabels(config.tier_labels),
|
||||
classifier_type: config.classifier_type,
|
||||
heuristic_v2_success_threshold: config.heuristic_v2_success_threshold,
|
||||
jev_classifier_config: config.classifier_type === "jev" ? config.jev_classifier_config : undefined,
|
||||
classifier_llm_config:
|
||||
config.classifier_type !== "jev" && config.classifier_llm_config
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue