Skip to content

Commit 6d4bd20

Browse files
committed
♻️ Improvement: Now embedding model in sdk will automatically retry up to 3 times when connection timeout
♻️ Improvement: Warn the user trying to proceed to next config page when embedding model has not connected yet ♻️ Improvement: Warn the user trying to modify the embedding config (from non-empty state to other state) ♻️ Improvement: Warn the user trying to proceed to next config page when embedding model has not set (regardless of multi-embedding model) ♻️ Refactor: add EmbedderCheckModal.tsx to manage all warning modals in model config page 🐛 Bug fix: Next.js warning in the home page: Instance created by `useForm` is not connected to any Form element.
1 parent b49a251 commit 6d4bd20

File tree

4 files changed

+107
-44
lines changed

4 files changed

+107
-44
lines changed

frontend/app/[locale]/setup/SetupLayout.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,16 @@ function Header({
5353
style={{ height: HEADER_CONFIG.HEIGHT }}
5454
>
5555
<div className="flex items-center">
56-
<button
56+
<Button
5757
onClick={() => router.push("/")}
5858
className="mr-3 p-2 rounded-full hover:bg-gray-100 dark:hover:bg-slate-800 transition-colors"
5959
aria-label={t("setup.header.button.back")}
60-
>
61-
<FiArrowLeft className="text-slate-600 dark:text-slate-300 text-xl" />
62-
</button>
60+
icon={
61+
<FiArrowLeft className="text-slate-600 dark:text-slate-300 text-xl" />
62+
}
63+
type="text"
64+
shape="circle"
65+
/>
6366
<h1 className="text-xl font-bold text-blue-600 dark:text-blue-500">
6467
{title}
6568
</h1>

frontend/app/[locale]/setup/models/components/modelConfig.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ export interface ModelConfigSectionRef {
7777
embedding?: ModelConnectStatus;
7878
multi_embedding?: ModelConnectStatus;
7979
};
80+
// Programmatically simulate a dropdown change and trigger onChange logic
81+
simulateDropdownChange: (
82+
category: string,
83+
option: string,
84+
displayName: string
85+
) => Promise<void>;
8086
}
8187

8288
interface ModelConfigSectionProps {
@@ -240,6 +246,14 @@ export const ModelConfigSection = forwardRef<
240246
verifyModels,
241247
getSelectedModels: () => selectedModels,
242248
getEmbeddingConnectivity,
249+
simulateDropdownChange: async (
250+
category: string,
251+
option: string,
252+
displayName: string
253+
) => {
254+
// Directly apply model change to mimic Select onChange behavior
255+
await applyModelChange(category, option, displayName);
256+
},
243257
}));
244258

245259
// Load model lists

frontend/app/[locale]/setup/models/config.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ interface AppModelConfigProps {
2626
// can add multi_embedding in future
2727
embedding?: string;
2828
}) => void;
29+
// Expose a ref from parent to allow programmatic dropdown change
30+
forwardedRef?: React.Ref<ModelConfigSectionRef>;
2931
}
3032

3133
export default function AppModelConfig({
3234
skipModelVerification = false,
3335
onSelectedModelsChange,
3436
onEmbeddingConnectivityChange,
37+
forwardedRef,
3538
}: AppModelConfigProps) {
3639
const { t } = useTranslation();
3740
const [isClientSide, setIsClientSide] = useState(false);
@@ -63,6 +66,17 @@ export default function AppModelConfig({
6366
return () => clearInterval(timer);
6467
}, [onSelectedModelsChange, onEmbeddingConnectivityChange]);
6568

69+
// Bridge internal ref to external forwardedRef so parent can call simulateDropdownChange
70+
useEffect(() => {
71+
if (!forwardedRef) return;
72+
if (typeof forwardedRef === 'function') {
73+
forwardedRef(modelConfigRef.current);
74+
} else {
75+
// @ts-ignore allow writing current
76+
(forwardedRef as any).current = modelConfigRef.current;
77+
}
78+
}, [forwardedRef]);
79+
6680
return (
6781
<div
6882
className="w-full mx-auto"

frontend/app/[locale]/setup/models/page.tsx

Lines changed: 72 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
"use client";
22

3-
import React, {useEffect, useState} from "react";
3+
import React, {useEffect, useRef, useState} from "react";
44
import {useRouter} from "next/navigation";
55
import {useTranslation} from "react-i18next";
66

7-
import {App, Button, Modal} from "antd";
8-
import {WarningFilled} from "@ant-design/icons";
7+
import {App, Button} from "antd";
98
import {motion} from "framer-motion";
109

1110
import {useAuth} from "@/hooks/useAuth";
1211
import modelEngineService from "@/services/modelEngineService";
1312
import {configStore} from "@/lib/config";
14-
import {CONNECTION_STATUS, ConnectionStatus,} from "@/const/modelConfig";
13+
import {
14+
CONNECTION_STATUS,
15+
ConnectionStatus,
16+
MODEL_STATUS
17+
} from "@/const/modelConfig";
1518
import log from "@/lib/logger";
1619

1720
import SetupLayout from "../SetupLayout";
1821
import AppModelConfig from "./config";
22+
import { ModelConfigSectionRef } from "./components/modelConfig";
23+
import EmbedderCheckModal from "./components/model/EmbedderCheckModal";
1924

2025
export default function ModelSetupPage() {
2126
const { message } = App.useApp();
@@ -27,13 +32,17 @@ export default function ModelSetupPage() {
2732
CONNECTION_STATUS.PROCESSING
2833
);
2934
const [isCheckingConnection, setIsCheckingConnection] = useState(false);
30-
const [isSaving, setIsSaving] = useState(false);
3135
const [embeddingModalOpen, setEmbeddingModalOpen] = useState(false);
3236
const [pendingJump, setPendingJump] = useState(false);
37+
const [connectivityWarningOpen, setConnectivityWarningOpen] = useState(false);
3338
const [liveSelectedModels, setLiveSelectedModels] = useState<Record<
3439
string,
3540
Record<string, string>
3641
> | null>(null);
42+
const [embeddingConnectivity, setEmbeddingConnectivity] = useState<{
43+
embedding?: string;
44+
} | null>(null);
45+
const modelConfigSectionRef = useRef<ModelConfigSectionRef | null>(null);
3746

3847
// Check login status and permission
3948
useEffect(() => {
@@ -98,6 +107,15 @@ export default function ModelSetupPage() {
98107
);
99108
return;
100109
}
110+
111+
// connectivity check for embedding models
112+
const connectivityOk = isEmbeddingConnectivityOk();
113+
if (!connectivityOk) {
114+
setConnectivityWarningOpen(true);
115+
setPendingJump(true);
116+
return;
117+
}
118+
101119
router.push("/setup/knowledges");
102120
} catch (error) {
103121
log.error(t("setup.page.error.systemError"), error);
@@ -113,6 +131,42 @@ export default function ModelSetupPage() {
113131
}
114132
};
115133

134+
// Check embedding connectivity for selected models
135+
const isEmbeddingConnectivityOk = (): boolean => {
136+
// can add multi_embedding in future
137+
const selectedEmbedding = liveSelectedModels?.embedding?.embedding || "";
138+
if (!selectedEmbedding) return true;
139+
const embStatus = embeddingConnectivity?.embedding;
140+
const ok = (s?: string) => !s || s === MODEL_STATUS.AVAILABLE;
141+
return ok(embStatus);
142+
};
143+
144+
const handleConnectivityOk = async () => {
145+
setConnectivityWarningOpen(false);
146+
if (pendingJump) {
147+
setPendingJump(false);
148+
// Apply live selections programmatically to mimic dropdown onChange
149+
try {
150+
const ref = modelConfigSectionRef.current;
151+
const selections = liveSelectedModels || {};
152+
if (ref && selections) {
153+
// Iterate categories and options
154+
for (const [category, options] of Object.entries(selections)) {
155+
for (const [option, displayName] of Object.entries(options)) {
156+
if (displayName) {
157+
// Simulate dropdown change and trigger onChange flow
158+
await ref.simulateDropdownChange(category, option, displayName);
159+
}
160+
}
161+
}
162+
}
163+
} catch (e) {
164+
message.error(t("setup.page.error.saveConfig"));
165+
}
166+
router.push("/setup/knowledges");
167+
}
168+
};
169+
116170
// Animation variants for smooth transitions
117171
const pageVariants = {
118172
initial: {
@@ -156,44 +210,22 @@ export default function ModelSetupPage() {
156210
>
157211
<AppModelConfig
158212
onSelectedModelsChange={(selected) => setLiveSelectedModels(selected)}
213+
onEmbeddingConnectivityChange={(status) => setEmbeddingConnectivity(status)}
214+
forwardedRef={modelConfigSectionRef}
159215
/>
160216
</motion.div>
161217

162-
<Modal
163-
title={t("embedding.emptyWarningModal.title")}
164-
open={embeddingModalOpen}
165-
onCancel={() => setEmbeddingModalOpen(false)}
166-
centered
167-
footer={
168-
<div className="flex justify-end mt-6 gap-4">
169-
<Button onClick={handleEmbeddingOk}>
170-
{t("embedding.emptyWarningModal.ok_continue")}
171-
</Button>
172-
<Button type="primary" onClick={() => setEmbeddingModalOpen(false)}>
173-
{t("embedding.emptyWarningModal.cancel")}
174-
</Button>
175-
</div>
176-
}
177-
>
178-
<div className="py-2">
179-
<div className="flex items-center">
180-
<WarningFilled
181-
className="text-yellow-500 mt-1 mr-2"
182-
style={{ fontSize: "48px" }}
183-
/>
184-
<div className="ml-3 mt-2">
185-
<div
186-
dangerouslySetInnerHTML={{
187-
__html: t("embedding.emptyWarningModal.content"),
188-
}}
189-
/>
190-
<div className="mt-2 text-xs opacity-70">
191-
{t("embedding.emptyWarningModal.tip")}
192-
</div>
193-
</div>
194-
</div>
195-
</div>
196-
</Modal>
218+
<EmbedderCheckModal
219+
emptyWarningOpen={embeddingModalOpen}
220+
onEmptyOk={handleEmbeddingOk}
221+
onEmptyCancel={() => setEmbeddingModalOpen(false)}
222+
connectivityWarningOpen={connectivityWarningOpen}
223+
onConnectivityOk={handleConnectivityOk}
224+
onConnectivityCancel={() => setConnectivityWarningOpen(false)}
225+
modifyWarningOpen={false}
226+
onModifyOk={() => {}}
227+
onModifyCancel={() => {}}
228+
/>
197229
</SetupLayout>
198230
);
199231
}

0 commit comments

Comments
 (0)