fix(ui): satisfy complexity router CI lint budgets

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yuneng 2026-09-21 19:56:30 +00:00
parent 9644032cb8
commit f70683ae92
5 changed files with 45 additions and 39 deletions

View file

@ -28,7 +28,7 @@ interface ComplexityRouterAdvancedSectionsProps {
onChange: (value: ComplexityRouterConfigValue) => void;
forecast: boolean;
modelOptions: { value: string; label: string }[];
classifierEffortOptionsByModel: Record<string, string[]>;
classifierEffortOptionsByModel: Record<string, string[] | null | undefined>;
customTechnicalKeywords?: string[];
onCustomTechnicalKeywordsChange?: (keywords: string[]) => void;
showValidationErrors: boolean;

View file

@ -408,6 +408,17 @@ const AddAutoRouterTab: React.FC<AddAutoRouterTabProps> = ({
matchThreshold,
escalationKeywords,
};
const jevRequestParams =
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;
const jevRequest = jevRequestParams ? buildAutoRouterRoutingTestRequest(jevRequestParams) : undefined;
const submitRecommendedRouter = async (name: string) => {
// The one answer the submit button reads, so a disabled button and a refused submit cannot
@ -816,20 +827,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={jevRequest}
onTestComplete={() => setIsTestingConnection(false)}
/>
</TooltipProvider>

View file

@ -744,16 +744,22 @@ export const buildComplexityRouterConfig = ({
open: open.trim().toLowerCase(),
close: close.trim().toLowerCase(),
}));
const cleanedListValues = {
code_keywords: cleanList(codeKeywords),
reasoning_keywords: cleanList(reasoningKeywords),
technical_keywords: cleanList(technicalKeywords),
simple_keywords: cleanList(simpleKeywords),
plan_mode_patterns: cleanList(planModePatterns),
housekeeping_patterns: cleanList(housekeepingPatterns),
};
const cleanedLists = Object.fromEntries(
Object.entries({
code_keywords: cleanList(codeKeywords),
reasoning_keywords: cleanList(reasoningKeywords),
technical_keywords: cleanList(technicalKeywords),
simple_keywords: cleanList(simpleKeywords),
plan_mode_patterns: cleanList(planModePatterns),
housekeeping_patterns: cleanList(housekeepingPatterns),
}).filter(([, list]) => list !== undefined),
Object.entries(cleanedListValues).filter(([, list]) => list !== undefined),
);
const hasValidCustomClassifierTimeout =
classifierType === "custom" &&
classifierPluginTimeoutMs !== undefined &&
Number.isInteger(classifierPluginTimeoutMs) &&
classifierPluginTimeoutMs > 0;
const supportsOpeningPrompt = !customTierSet && !forecast && usesLlmClassifier(effectiveType);
const payload: ComplexityRouterConfigPayload = {
@ -826,10 +832,7 @@ export const buildComplexityRouterConfig = ({
...(routeHousekeepingToCheapestTier === false && { route_housekeeping_to_cheapest_tier: false }),
...(cleanedReminderMarkers && cleanedReminderMarkers.length > 0 && { reminder_markers: cleanedReminderMarkers }),
...(maxTokensFromTierModel === false && { max_tokens_from_tier_model: false }),
...(classifierType === "custom" &&
classifierPluginTimeoutMs !== undefined &&
Number.isInteger(classifierPluginTimeoutMs) &&
classifierPluginTimeoutMs > 0 && { classifier_plugin_timeout_ms: classifierPluginTimeoutMs }),
...(hasValidCustomClassifierTimeout && { classifier_plugin_timeout_ms: classifierPluginTimeoutMs }),
...scorerKnobs,
};
if (!customTierSet) return payload;

View file

@ -373,12 +373,13 @@ const EditAutoRouterModal: React.FC<EditAutoRouterModalProps> = ({
setRouterConfig(parsedConfig);
// Set form values
form.reset({
const routerFormValues = {
auto_router_name: modelData.model_name,
auto_router_default_model: modelData.litellm_params?.auto_router_default_model || null,
auto_router_embedding_model: modelData.litellm_params?.auto_router_embedding_model || null,
model_access_group: modelData.model_info?.access_groups || [],
});
};
form.reset(routerFormValues);
} catch (error) {
console.error("Error parsing auto router config:", error);
toast.fromError("Error loading auto router configuration");
@ -456,11 +457,12 @@ const EditAutoRouterModal: React.FC<EditAutoRouterModalProps> = ({
// Dual write: complexity_router_config.default_model (the pin marker hydratePinnedDefaultModel
// reads back) and complexity_router_default_model (what the backend routes on) must always be
// written together from the same value. Same pairing in add_auto_router_tab.tsx.
const keywordMatching = { keywordTierRules, escalationKeywords, semanticMatchingEnabled, embeddingModel, matchThreshold };
const updatedConfig = buildUpdatedComplexityRouterConfig(
modelData.litellm_params?.complexity_router_config,
complexityRouterConfig,
customTechnicalKeywords,
{ keywordTierRules, escalationKeywords, semanticMatchingEnabled, embeddingModel, matchThreshold },
keywordMatching,
);
const serverVerdict = await validateAutoRouterConfig(accessToken, updatedConfig, modelData?.model_info?.team_id);
const dryRunError = dryRunRejection(serverVerdict);
@ -497,12 +499,13 @@ const EditAutoRouterModal: React.FC<EditAutoRouterModalProps> = ({
);
toast.success("Auto router configuration updated successfully");
onSuccess({
const updatedModelData = {
...modelData,
model_name: values.auto_router_name,
litellm_params: updatedLitellmParams,
model_info: updatedModelInfo,
});
};
onSuccess(updatedModelData);
onCancel();
return;
}

View file

@ -26,13 +26,15 @@ import {
const isReminderMarkerPair = (
input: unknown,
): input is { open: string; close: string } =>
typeof input === "object" &&
input !== null &&
"open" in input &&
"close" in input &&
typeof input.open === "string" &&
typeof input.close === "string";
): input is { open: string; close: string } => {
if (typeof input !== "object" || input === null) {
return false;
}
if (!("open" in input) || !("close" in input)) {
return false;
}
return typeof input.open === "string" && typeof input.close === "string";
};
const stringList = (input: unknown): string[] | undefined =>
Array.isArray(input) ? input.filter((item): item is string => typeof item === "string") : undefined;