Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions crates/agent-gui/test/settings/provider-models-fetch.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -616,20 +616,16 @@ test("mergeFetchedModels never overwrites a user-edited stored value with a fres
assert.equal(model.limitsSource, "user");
});

test("model bulk helpers count and apply only selected active states", () => {
test("applyModelsActiveState applies enable/disable to target models only", () => {
const activeModels = new Set(["enabled-model", "untouched-model"]);
const selectedModels = new Set(["enabled-model", "disabled-model"]);
const targetModels = ["enabled-model", "disabled-model"];

assert.deepEqual(providerUtils.getModelBulkActionCounts(selectedModels, activeModels), {
enableCount: 1,
disableCount: 1,
});
assert.deepEqual(
[...providerUtils.applyModelBulkActiveState(activeModels, selectedModels, true)].sort(),
[...providerUtils.applyModelsActiveState(activeModels, targetModels, true)].sort(),
["disabled-model", "enabled-model", "untouched-model"],
);
assert.deepEqual(
[...providerUtils.applyModelBulkActiveState(activeModels, selectedModels, false)].sort(),
[...providerUtils.applyModelsActiveState(activeModels, targetModels, false)].sort(),
["untouched-model"],
);
assert.deepEqual([...activeModels].sort(), ["enabled-model", "untouched-model"]);
Expand Down
5 changes: 4 additions & 1 deletion crates/agent-ui/src/i18n/translations/enUSSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,9 +626,12 @@ export const EN_US_SETTINGS_TRANSLATIONS = {
"settings.reorderModel": "Reorder model",
"settings.reorderProvider": "Reorder provider",
"settings.reorderVerticalHint": "Drag to reorder; touch and hold on mobile, or use Up/Down",
"settings.modelReorderDisabledBulk": "Reordering is unavailable in bulk selection mode",
"settings.modelReorderDisabledSearch": "Reordering is unavailable while filtering",
"settings.reorderNeedsTwoItems": "At least two items are required to reorder",
"settings.enableAllModels": "Enable all models",
"settings.disableAllModels": "Disable all models",
"settings.modelsEnabledCount": "{enabled} / {total} models enabled",
"settings.matchedModelsEnabledCount": "{enabled} / {total} matched models enabled",
"settings.fetching": "Fetching…",
"settings.deselectAll": "Deselect All",
"settings.selectAll": "Select All",
Expand Down
5 changes: 4 additions & 1 deletion crates/agent-ui/src/i18n/translations/zhCNSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,9 +600,12 @@ export const ZH_CN_SETTINGS_TRANSLATIONS = {
"settings.reorderModel": "调整模型排序",
"settings.reorderProvider": "调整供应商排序",
"settings.reorderVerticalHint": "拖动排序;手机端长按后拖动,或聚焦后按上下方向键移动",
"settings.modelReorderDisabledBulk": "批量选择模式下不能拖拽排序",
"settings.modelReorderDisabledSearch": "搜索过滤时不能拖拽排序",
"settings.reorderNeedsTwoItems": "至少需要两项才能排序",
"settings.enableAllModels": "启用全部模型",
"settings.disableAllModels": "禁用全部模型",
"settings.modelsEnabledCount": "已启用 {enabled} / {total} 个模型",
"settings.matchedModelsEnabledCount": "匹配的模型已启用 {enabled} / {total} 个",
"settings.fetching": "获取中…",
"settings.deselectAll": "取消全选",
"settings.selectAll": "全选",
Expand Down
93 changes: 22 additions & 71 deletions crates/agent-ui/src/pages/settings/ProviderModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,15 @@ import {
findNewModelIds,
} from "@liveagent/ui/lib/providers/modelVendor";
import {
applyModelBulkActiveState,
applyModelInputModalitiesMode,
applyModelsActiveState,
applyUsageQueryModePreset,
buildProviderModelsFetchKey,
clampUsageQueryTimeoutSecs,
createDraftModelConfig,
createUsageQueryDraft,
detectCodingPlanProvider,
fetchModelsFromApi,
getModelBulkActionCounts,
getModelInputModalitiesMode,
getPersistedUsageQueryProviderId,
isGatewayWebuiRuntime,
Expand Down Expand Up @@ -220,10 +219,6 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
const [addingModel, setAddingModel] = useState(false);
const [newModelName, setNewModelName] = useState("");
const [modelSearch, setModelSearch] = useState("");
const [modelBulkMode, setModelBulkMode] = useState(false);
const [modelBulkSelection, setModelBulkSelection] = useState<ReadonlySet<string>>(
() => new Set(),
);
const [editingModel, setEditingModel] = useState<ModelEditDraft | null>(null);
const [showApiKey, setShowApiKey] = useState(false);
const [activePanel, setActivePanel] = useState<ProviderDialogPanel>("general");
Expand Down Expand Up @@ -504,44 +499,6 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
});
}

function exitModelBulkMode() {
setModelBulkMode(false);
setModelBulkSelection(new Set());
}

function toggleModelBulkMode() {
if (modelBulkMode) {
exitModelBulkMode();
return;
}
setEditingModel(null);
setAddingModel(false);
setModelBulkSelection(new Set());
setModelBulkMode(true);
}

function toggleModelBulkSelection(modelId: string) {
setModelBulkSelection((prev) => {
const next = new Set(prev);
if (next.has(modelId)) next.delete(modelId);
else next.add(modelId);
return next;
});
}

function selectVisibleModels() {
setModelBulkSelection((prev) => {
const next = new Set(prev);
for (const model of visibleModels) next.add(model.id);
return next;
});
}

function applyModelBulkState(enabled: boolean) {
setActiveModels((prev) => applyModelBulkActiveState(prev, modelBulkSelection, enabled));
setModelBulkSelection(new Set());
}

function handleAddModel() {
const model = newModelName.trim();
if (!model) return;
Expand All @@ -568,12 +525,6 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
next.delete(model);
return next;
});
setModelBulkSelection((prev) => {
if (!prev.has(model)) return prev;
const next = new Set(prev);
next.delete(model);
return next;
});
setEditingModel((prev) => (prev?.model.id === model ? null : prev));
}

Expand Down Expand Up @@ -738,7 +689,6 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
);
if (invalidHeaderIndex >= 0) {
setHeaderValidationSubmitted(true);
exitModelBulkMode();
setActivePanel("request");
// 导入视图会顶掉请求头列表,先切回列表再聚焦,否则目标输入框尚未挂载。
setHeaderImportOpen(false);
Expand Down Expand Up @@ -869,15 +819,24 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
: orderedModels,
[orderedModels, modelSearchQuery],
);
const allVisibleModelsSelected =
visibleModels.length > 0 && visibleModels.every((model) => modelBulkSelection.has(model.id));
const { enableCount: modelBulkEnableCount, disableCount: modelBulkDisableCount } = useMemo(
() => getModelBulkActionCounts(modelBulkSelection, activeModels),
[modelBulkSelection, activeModels],
// 表头总开关:作用于当前可见(含搜索过滤)的模型。全部启用时视为“开”,
// 再点一次全部禁用;部分启用时点击补全为全部启用。
const visibleActiveCount = useMemo(
() => visibleModels.reduce((count, model) => count + (activeModels.has(model.id) ? 1 : 0), 0),
[visibleModels, activeModels],
);
const modelReorderDisabledHint = modelBulkMode
? t("settings.modelReorderDisabledBulk")
: modelSearchQuery
const allVisibleModelsActive =
visibleModels.length > 0 && visibleActiveCount === visibleModels.length;
function toggleVisibleModelsActive() {
setActiveModels((prev) =>
applyModelsActiveState(
prev,
visibleModels.map((model) => model.id),
!allVisibleModelsActive,
),
);
}
const modelReorderDisabledHint = modelSearchQuery
? t("settings.modelReorderDisabledSearch")
: t("settings.reorderNeedsTwoItems");
const handleModelReorder = useCallback((nextIds: string[]) => {
Expand All @@ -896,7 +855,7 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
scrollContainerRef: modelScrollContainerRef,
} = useVerticalListReorder({
itemIds: orderedModels.map((model) => model.id),
canReorder: !modelBulkMode && !modelSearchQuery,
canReorder: !modelSearchQuery,
reorderLabel: t("settings.reorderModel"),
reorderHint: t("settings.reorderVerticalHint"),
disabledHint: modelReorderDisabledHint,
Expand Down Expand Up @@ -966,13 +925,12 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
activePanel,
addCustomHeader,
addingModel,
allVisibleModelsSelected,
allVisibleModelsActive,
apiKey,
apiKeyForRequest,
apiKeyIsRedactedDisplay,
applyHeaderSuggestion,
applyCliIdentityHeaders,
applyModelBulkState,
baseUrl,
canSaveEditingModel,
canOverrideModelInputModalities,
Expand All @@ -984,7 +942,6 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
editingModelContextWindow,
editingModelInputModalitiesMode,
editingModelMaxOutputToken,
exitModelBulkMode,
fetchError,
fetchingModels,
focusCustomHeader,
Expand All @@ -1010,10 +967,6 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
isFullUrl,
isGatewayWebui,
matchedBalanceProviders,
modelBulkDisableCount,
modelBulkEnableCount,
modelBulkMode,
modelBulkSelection,
modelListRef,
modelScrollContainerRef,
modelSearch,
Expand All @@ -1037,7 +990,6 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
requestClose,
requestFormat,
saveInlineModelSettings,
selectVisibleModels,
setActivePanel,
setAddingModel,
setApiKey,
Expand All @@ -1051,7 +1003,6 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
setHeaderSuggest,
setHeaderSuggestActive,
setIsFullUrl,
setModelBulkSelection,
setModelSearch,
setModelsUrl,
setName,
Expand All @@ -1074,8 +1025,7 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
commitStreamRetryCountInput,
t,
toggleModel,
toggleModelBulkMode,
toggleModelBulkSelection,
toggleVisibleModelsActive,
typeLabel,
updateCustomHeader,
usageQuery,
Expand All @@ -1085,6 +1035,7 @@ function useProviderModalController({ providerType, initialData, onSave, onClose
usageVariableApiKey,
usageVariableBaseUrl,
useSystemProxy,
visibleActiveCount,
visibleModels,
};
return viewModel;
Expand Down
Loading
Loading