Skip to content

Commit 1ca3dcf

Browse files
authored
Merge pull request #1751 from ModelEngine-Group/wmc/bugfix_1117
2 parents 321b257 + 1ffe066 commit 1ca3dcf

File tree

3 files changed

+67
-46
lines changed

3 files changed

+67
-46
lines changed

frontend/app/[locale]/models/components/appConfig.tsx

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useRef, useState, useEffect } from 'react';
1+
import React, { useRef, useState, useEffect, useCallback } from 'react';
22
import dynamic from 'next/dynamic';
33
import { useTranslation } from 'react-i18next';
44

@@ -39,8 +39,6 @@ export const AppConfigSection: React.FC = () => {
3939
// Add user input state tracking
4040
const isUserTypingAppName = useRef(false);
4141
const isUserTypingDescription = useRef(false);
42-
const appNameUpdateTimer = useRef<NodeJS.Timeout | null>(null);
43-
const descriptionUpdateTimer = useRef<NodeJS.Timeout | null>(null);
4442

4543
// Avatar-related state
4644
const [isAvatarModalOpen, setIsAvatarModalOpen] = useState(false);
@@ -67,6 +65,22 @@ export const AppConfigSection: React.FC = () => {
6765

6866
const fileInputRef = useRef<HTMLInputElement>(null);
6967

68+
const triggerAutoSave = useCallback(() => {
69+
const runSave = async () => {
70+
try {
71+
const ok = await configService.saveConfigToBackend(getConfig() as any);
72+
if (!ok) {
73+
message.error(t("setup.page.error.saveConfig"));
74+
}
75+
} catch (error) {
76+
message.error(t("setup.page.error.saveConfig"));
77+
log.error("Failed to auto save app configuration", error);
78+
}
79+
};
80+
81+
void runSave();
82+
}, [getConfig, message, t]);
83+
7084
// Add configuration change listener, synchronize local state when config is loaded from backend
7185
useEffect(() => {
7286
const handleConfigChanged = (event: any) => {
@@ -139,18 +153,6 @@ export const AppConfigSection: React.FC = () => {
139153
};
140154
}, []);
141155

142-
// Clean up timers
143-
useEffect(() => {
144-
return () => {
145-
if (appNameUpdateTimer.current) {
146-
clearTimeout(appNameUpdateTimer.current);
147-
}
148-
if (descriptionUpdateTimer.current) {
149-
clearTimeout(descriptionUpdateTimer.current);
150-
}
151-
};
152-
}, []);
153-
154156
// Handle basic app config changes
155157
const handleAppNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
156158
const newAppName = e.target.value;
@@ -162,25 +164,12 @@ export const AppConfigSection: React.FC = () => {
162164
setAppNameError(false);
163165
}
164166

165-
// Clear previous timer
166-
if (appNameUpdateTimer.current) {
167-
clearTimeout(appNameUpdateTimer.current);
168-
}
169-
170-
// Set debounced update
171-
appNameUpdateTimer.current = setTimeout(() => {
172-
updateAppConfig({ appName: newAppName });
173-
isUserTypingAppName.current = false;
174-
}, 500);
175167
};
176168

177169
const handleAppNameBlur = () => {
178-
// Clear timer, update immediately
179-
if (appNameUpdateTimer.current) {
180-
clearTimeout(appNameUpdateTimer.current);
181-
}
182170
updateAppConfig({ appName: localAppName });
183171
isUserTypingAppName.current = false;
172+
triggerAutoSave();
184173
};
185174

186175
const handleDescriptionChange = (
@@ -189,26 +178,12 @@ export const AppConfigSection: React.FC = () => {
189178
const newDescription = e.target.value;
190179
isUserTypingDescription.current = true;
191180
setLocalAppDescription(newDescription);
192-
193-
// Clear previous timer
194-
if (descriptionUpdateTimer.current) {
195-
clearTimeout(descriptionUpdateTimer.current);
196-
}
197-
198-
// Set debounced update
199-
descriptionUpdateTimer.current = setTimeout(() => {
200-
updateAppConfig({ appDescription: newDescription });
201-
isUserTypingDescription.current = false;
202-
}, 500);
203181
};
204182

205183
const handleDescriptionBlur = () => {
206-
// Clear timer, update immediately
207-
if (descriptionUpdateTimer.current) {
208-
clearTimeout(descriptionUpdateTimer.current);
209-
}
210184
updateAppConfig({ appDescription: localAppDescription });
211185
isUserTypingDescription.current = false;
186+
triggerAutoSave();
212187
};
213188

214189
// Open avatar selection modal

frontend/app/[locale]/models/components/model/ModelAddDialog.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,8 @@ export const ModelAddDialog = ({
10931093
open={settingsModalVisible}
10941094
onCancel={() => setSettingsModalVisible(false)}
10951095
onOk={handleSettingsSave}
1096+
cancelText={t("common.cancel")}
1097+
okText={t("common.ok")}
10961098
destroyOnClose
10971099
>
10981100
<div className="space-y-3">

frontend/app/[locale]/models/components/model/ModelDeleteDialog.tsx

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from "react";
1+
import { useMemo, useState } from "react";
22
import { useTranslation } from "react-i18next";
33

44
import { Modal, Button, Switch, App, Tooltip, Input } from "antd";
@@ -57,6 +57,7 @@ export const ModelDeleteDialog = ({
5757
const [selectedModelForSettings, setSelectedModelForSettings] =
5858
useState<any>(null);
5959
const [modelMaxTokens, setModelMaxTokens] = useState("4096");
60+
const [providerModelSearchTerm, setProviderModelSearchTerm] = useState("");
6061

6162
// Get model color scheme
6263
const getModelColorScheme = (
@@ -305,6 +306,7 @@ export const ModelDeleteDialog = ({
305306
return;
306307
}
307308
setSelectedSource(source);
309+
setProviderModelSearchTerm("");
308310
};
309311

310312
const handleEditModel = (model: ModelOption) => {
@@ -415,8 +417,28 @@ export const ModelDeleteDialog = ({
415417
setProviderModels([]);
416418
setPendingSelectedProviderIds(new Set());
417419
setMaxTokens(0);
420+
setProviderModelSearchTerm("");
418421
onClose();
419422
};
423+
const filteredProviderModels = useMemo(() => {
424+
const keyword = providerModelSearchTerm.trim().toLowerCase();
425+
if (!keyword) {
426+
return providerModels;
427+
}
428+
return providerModels.filter((model) => {
429+
const candidates = [
430+
model?.id,
431+
model?.model_name,
432+
model?.model_tag,
433+
model?.description,
434+
];
435+
return candidates.some(
436+
(text) =>
437+
typeof text === "string" && text.toLowerCase().includes(keyword)
438+
);
439+
});
440+
}, [providerModels, providerModelSearchTerm]);
441+
420442

421443
// Handle provider config save
422444
const handleProviderConfigSave = async ({
@@ -621,6 +643,7 @@ export const ModelDeleteDialog = ({
621643
onClick={() => {
622644
setDeletingModelType(type);
623645
setSelectedSource(null);
646+
setProviderModelSearchTerm("");
624647
// Initialize maxTokens with a value from existing models of this type
625648
const existingModel = customModels.find(
626649
(model) => model.type === type
@@ -768,6 +791,7 @@ export const ModelDeleteDialog = ({
768791
onClick={() => {
769792
setSelectedSource(null);
770793
setProviderModels([]);
794+
setProviderModelSearchTerm("");
771795
}}
772796
className="text-blue-500 hover:text-blue-700 flex items-center"
773797
>
@@ -819,7 +843,25 @@ export const ModelDeleteDialog = ({
819843
{selectedSource === MODEL_SOURCES.SILICON &&
820844
providerModels.length > 0 ? (
821845
<div className="max-h-60 overflow-y-auto border border-gray-200 rounded-md divide-y divide-gray-200">
822-
{providerModels.map((providerModel: any) => {
846+
{providerModels.length > 0 && (
847+
<div className="sticky top-0 z-10 bg-white p-2">
848+
<Input
849+
allowClear
850+
size="small"
851+
placeholder={t("model.dialog.modelList.searchPlaceholder")}
852+
value={providerModelSearchTerm}
853+
onChange={(event) =>
854+
setProviderModelSearchTerm(event.target.value)
855+
}
856+
/>
857+
</div>
858+
)}
859+
{filteredProviderModels.length === 0 && (
860+
<div className="p-4 text-center text-xs text-gray-500">
861+
{t("model.dialog.modelList.noResults")}
862+
</div>
863+
)}
864+
{filteredProviderModels.map((providerModel: any) => {
823865
const checked = pendingSelectedProviderIds.has(
824866
providerModel.id
825867
);
@@ -1017,6 +1059,8 @@ export const ModelDeleteDialog = ({
10171059
open={settingsModalVisible}
10181060
onCancel={() => setSettingsModalVisible(false)}
10191061
onOk={handleSettingsSave}
1062+
cancelText={t("common.button.cancel")}
1063+
okText={t("common.button.save")}
10201064
destroyOnClose
10211065
>
10221066
<div className="space-y-3">

0 commit comments

Comments
 (0)