Skip to content

Commit ddb18c7

Browse files
authored
πŸ› no expected error should show on UI
2 parents 3ead61c + 62c3566 commit ddb18c7

File tree

13 files changed

+374
-171
lines changed

13 files changed

+374
-171
lines changed

β€Žbackend/apps/model_managment_app.pyβ€Ž

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,25 +157,35 @@ async def get_provider_list(request: ProviderModelRequest, authorization: Option
157157

158158

159159
@router.post("/update")
160-
async def update_single_model(request: dict, authorization: Optional[str] = Header(None)):
161-
"""Update a single model by its `model_id`.
160+
async def update_single_model(
161+
request: dict,
162+
display_name: str = Query(..., description="Current display name of the model to update"),
163+
authorization: Optional[str] = Header(None)
164+
):
165+
"""Update a single model by its current `display_name`.
162166
163-
Performs a uniqueness check on `display_name` within the tenant and updates
164-
the record if valid.
167+
The model is looked up using the `display_name` query parameter. The request
168+
body contains the fields to update, which may include a new `display_name`.
165169
166170
Args:
167-
request: Arbitrary model fields with required `model_id`.
171+
request: Arbitrary model fields to update (may include new display_name).
172+
display_name: Current display name of the model (query parameter for lookup).
168173
authorization: Bearer token header used to derive identity context.
169174
170175
Raises:
171-
HTTPException: 409 if `display_name` conflicts, 500 for unexpected errors.
176+
HTTPException: 404 if model not found, 409 if new `display_name` conflicts,
177+
500 for unexpected errors.
172178
"""
173179
try:
174180
user_id, tenant_id = get_current_user_id(authorization)
175-
await update_single_model_for_tenant(user_id, tenant_id, request)
181+
await update_single_model_for_tenant(user_id, tenant_id, display_name, request)
176182
return JSONResponse(status_code=HTTPStatus.OK, content={
177183
"message": "Model updated successfully"
178184
})
185+
except LookupError as e:
186+
logging.error(f"Failed to update model: {str(e)}")
187+
raise HTTPException(status_code=HTTPStatus.NOT_FOUND,
188+
detail=str(e))
179189
except ValueError as e:
180190
logging.error(f"Failed to update model: {str(e)}")
181191
raise HTTPException(status_code=HTTPStatus.CONFLICT,

β€Žbackend/database/model_management_db.pyβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,21 @@ def get_model_by_display_name(display_name: str, tenant_id: str) -> Optional[Dic
185185
return model
186186

187187

188+
def get_models_by_display_name(display_name: str, tenant_id: str) -> List[Dict[str, Any]]:
189+
"""
190+
Get all model records by display name (for multi_embedding which creates two records)
191+
192+
Args:
193+
display_name: Model display name
194+
tenant_id: Tenant ID
195+
196+
Returns:
197+
List[Dict[str, Any]]: List of model records with the same display_name
198+
"""
199+
filters = {'display_name': display_name}
200+
return get_model_records(filters, tenant_id)
201+
202+
188203
def get_model_id_by_display_name(display_name: str, tenant_id: str) -> Optional[int]:
189204
"""
190205
Get a model ID by display name

β€Žbackend/services/model_management_service.pyβ€Ž

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
create_model_record,
1010
delete_model_record,
1111
get_model_by_display_name,
12+
get_models_by_display_name,
1213
get_model_records,
1314
get_models_by_tenant_factory_type,
1415
update_model_record,
@@ -198,20 +199,63 @@ async def list_provider_models_for_tenant(tenant_id: str, provider: str, model_t
198199
raise Exception(f"Failed to list provider models: {str(e)}")
199200

200201

201-
async def update_single_model_for_tenant(user_id: str, tenant_id: str, model_data: Dict[str, Any]):
202-
"""Update a single model by its model_id, ensuring display_name uniqueness."""
202+
async def update_single_model_for_tenant(
203+
user_id: str,
204+
tenant_id: str,
205+
current_display_name: str,
206+
model_data: Dict[str, Any]
207+
):
208+
"""Update model(s) by current display_name. If embedding/multi_embedding, update both types.
209+
210+
Args:
211+
user_id: The user performing the update.
212+
tenant_id: The tenant context.
213+
current_display_name: The current display_name used to look up the model(s).
214+
model_data: The fields to update, which may include a new display_name.
215+
216+
Raises:
217+
LookupError: If no model is found with the current_display_name.
218+
ValueError: If a new display_name conflicts with an existing model.
219+
"""
203220
try:
204-
existing_model_by_display = get_model_by_display_name(model_data["display_name"], tenant_id)
205-
current_model_id = int(model_data["model_id"])
206-
existing_model_id = existing_model_by_display["model_id"] if existing_model_by_display else None
207-
208-
if existing_model_by_display and existing_model_id != current_model_id:
209-
raise ValueError(
210-
f"Name {model_data['display_name']} is already in use, please choose another display name")
221+
# Get all models with the current display_name (may be 1 or 2 for embedding types)
222+
existing_models = get_models_by_display_name(current_display_name, tenant_id)
211223

212-
update_model_record(current_model_id, model_data, user_id)
213-
logging.debug(
214-
f"Model {model_data['display_name']} updated successfully")
224+
if not existing_models:
225+
raise LookupError(f"Model not found: {current_display_name}")
226+
227+
# Check if a new display_name is being set and if it conflicts
228+
new_display_name = model_data.get("display_name")
229+
if new_display_name and new_display_name != current_display_name:
230+
conflict_models = get_models_by_display_name(new_display_name, tenant_id)
231+
if conflict_models:
232+
raise ValueError(
233+
f"Name {new_display_name} is already in use, please choose another display name"
234+
)
235+
236+
# Check if any of the existing models is multi_embedding
237+
has_multi_embedding = any(
238+
m.get("model_type") == "multi_embedding" for m in existing_models
239+
)
240+
241+
if has_multi_embedding:
242+
# Update both embedding and multi_embedding records
243+
for model in existing_models:
244+
# Prepare update data, excluding model_type to preserve original type
245+
update_data = {k: v for k, v in model_data.items() if k not in ["model_id", "model_type"]}
246+
update_model_record(model["model_id"], update_data, user_id)
247+
logging.debug(
248+
f"Model {current_display_name} (embedding + multi_embedding) updated successfully")
249+
else:
250+
# Single model update
251+
current_model_id = existing_models[0]["model_id"]
252+
update_data = {k: v for k, v in model_data.items() if k != "model_id"}
253+
update_model_record(current_model_id, update_data, user_id)
254+
logging.debug(f"Model {current_display_name} updated successfully")
255+
except LookupError:
256+
raise
257+
except ValueError:
258+
raise
215259
except Exception as e:
216260
logging.error(f"Failed to update model: {str(e)}")
217261
raise Exception(f"Failed to update model: {str(e)}")
@@ -221,7 +265,7 @@ async def batch_update_models_for_tenant(user_id: str, tenant_id: str, model_lis
221265
"""Batch update models for a tenant."""
222266
try:
223267
for model in model_list:
224-
update_model_record(model["model_id"], model, user_id)
268+
update_model_record(model["model_id"], model, user_id, tenant_id)
225269

226270
logging.debug("Batch update models successfully")
227271
except Exception as e:
@@ -232,24 +276,24 @@ async def batch_update_models_for_tenant(user_id: str, tenant_id: str, model_lis
232276
async def delete_model_for_tenant(user_id: str, tenant_id: str, display_name: str):
233277
"""Delete model(s) by display_name. If embedding/multi_embedding, delete both types."""
234278
try:
235-
model = get_model_by_display_name(display_name, tenant_id)
236-
if not model:
279+
# Get all models with this display_name (may be 1 or 2 for embedding types)
280+
models = get_models_by_display_name(display_name, tenant_id)
281+
if not models:
237282
raise LookupError(f"Model not found: {display_name}")
238283

239284
deleted_types: List[str] = []
240-
if model.get("model_type") in ["embedding", "multi_embedding"]:
241-
# Fetch both variants once to avoid repeated lookups
242-
models_by_type: Dict[str, Dict[str, Any]] = {}
243-
for t in ["embedding", "multi_embedding"]:
244-
m = get_model_by_display_name(display_name, tenant_id)
245-
if m and m.get("model_type") == t:
246-
models_by_type[t] = m
247-
248-
# Best-effort memory cleanup using the fetched variants
285+
286+
# Check if any of the models is multi_embedding (which means we have both types)
287+
has_multi_embedding = any(
288+
m.get("model_type") == "multi_embedding" for m in models
289+
)
290+
291+
if has_multi_embedding:
292+
# Best-effort memory cleanup for embedding models
249293
try:
250294
vdb_core = get_vector_db_core()
251295
base_memory_config = build_memory_config_for_tenant(tenant_id)
252-
for t, m in models_by_type.items():
296+
for m in models:
253297
try:
254298
await clear_model_memories(
255299
vdb_core=vdb_core,
@@ -270,17 +314,21 @@ async def delete_model_for_tenant(user_id: str, tenant_id: str, display_name: st
270314
logger.warning(
271315
"Memory cleanup preparation failed: %s", outer_cleanup_exc)
272316

273-
# Delete the fetched variants
274-
for t, m in models_by_type.items():
317+
# Delete all records with the same display_name
318+
for m in models:
275319
delete_model_record(m["model_id"], user_id, tenant_id)
276-
deleted_types.append(t)
320+
deleted_types.append(m.get("model_type", "unknown"))
277321
else:
322+
# Single model delete
323+
model = models[0]
278324
delete_model_record(model["model_id"], user_id, tenant_id)
279325
deleted_types.append(model.get("model_type", "unknown"))
280326

281327
logging.debug(
282328
f"Successfully deleted model(s) in types: {', '.join(deleted_types)}")
283329
return display_name
330+
except LookupError:
331+
raise
284332
except Exception as e:
285333
logging.error(f"Failed to delete model: {str(e)}")
286334
raise Exception(f"Failed to delete model: {str(e)}")

β€Žfrontend/app/[locale]/agents/components/tool/ToolPool.tsxβ€Ž

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -650,15 +650,19 @@ function ToolPool({
650650
{t("toolPool.tooltip.functionGuide")}
651651
</div>
652652
}
653-
overlayInnerStyle={{
654-
backgroundColor: "#ffffff",
655-
color: "#374151",
656-
border: "1px solid #e5e7eb",
657-
borderRadius: "6px",
658-
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
659-
padding: "12px",
660-
maxWidth: "600px",
661-
minWidth: "400px",
653+
color="#ffffff"
654+
styles={{
655+
body: {
656+
backgroundColor: "#ffffff",
657+
color: "#374151",
658+
border: "1px solid #e5e7eb",
659+
borderRadius: "6px",
660+
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
661+
padding: "12px",
662+
maxWidth: "800px",
663+
minWidth: "700px",
664+
width: "fit-content",
665+
},
662666
}}
663667
>
664668
<BulbOutlined className="ml-2 text-yellow-500" />

β€Žfrontend/app/[locale]/chat/streaming/taskWindow.tsxβ€Ž

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,19 @@ export function TaskWindow({ messages, isStreaming = false }: TaskWindowProps) {
967967
);
968968
};
969969

970+
// Error messages that should be completely hidden (including the node)
971+
const suppressedErrorMessages = [
972+
"Model is interrupted by stop event",
973+
"Agent execution interrupted by external stop signal",
974+
];
975+
976+
// Check if a message should be suppressed (not displayed at all)
977+
const shouldSuppressMessage = (message: any) => {
978+
if (message.type !== "error") return false;
979+
const content = message.content || "";
980+
return suppressedErrorMessages.some((errText) => content.includes(errText));
981+
};
982+
970983
// Check if it is the last message
971984
const isLastMessage = (index: number, messages: any[]) => {
972985
return index === messages.length - 1;
@@ -996,15 +1009,20 @@ export function TaskWindow({ messages, isStreaming = false }: TaskWindowProps) {
9961009
);
9971010
}
9981011

1012+
// Filter out messages that should be suppressed
1013+
const filteredGroupedMessages = groupedMessages.filter(
1014+
(group) => !shouldSuppressMessage(group.message)
1015+
);
1016+
9991017
return (
10001018
<div className="relative">
10011019
<div className="absolute left-[0.2rem] top-[1.25rem] bottom-0 w-0.5 bg-gray-200"></div>
10021020

1003-
{groupedMessages.map((group, groupIndex) => {
1021+
{filteredGroupedMessages.map((group, groupIndex) => {
10041022
const message = group.message;
10051023
const isBlinking = shouldBlinkDot(
10061024
groupIndex,
1007-
groupedMessages.map((g) => g.message)
1025+
filteredGroupedMessages.map((g) => g.message)
10081026
);
10091027

10101028
return (

β€Žfrontend/app/[locale]/knowledges/KnowledgeBaseConfiguration.tsxβ€Ž

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,40 @@ function DataConfig({ isActive }: DataConfigProps) {
757757
setNewKbName(name);
758758
};
759759

760+
// If Embedding model is not configured, show warning container instead of content
761+
if (showEmbeddingWarning) {
762+
return (
763+
<div
764+
className="w-full mx-auto relative"
765+
style={{
766+
maxWidth: SETUP_PAGE_CONTAINER.MAX_WIDTH,
767+
padding: `0 ${SETUP_PAGE_CONTAINER.HORIZONTAL_PADDING}`,
768+
}}
769+
>
770+
<div
771+
className={STANDARD_CARD.BASE_CLASSES}
772+
style={{
773+
height: SETUP_PAGE_CONTAINER.MAIN_CONTENT_HEIGHT,
774+
padding: STANDARD_CARD.PADDING,
775+
display: "flex",
776+
alignItems: "center",
777+
justifyContent: "center",
778+
}}
779+
>
780+
<div className="text-center">
781+
<WarningFilled
782+
className="text-yellow-500 mb-4"
783+
style={{ fontSize: 48 }}
784+
/>
785+
<div className="text-base text-gray-800 font-semibold">
786+
{t("embedding.knowledgeBaseDisabledWarningModal.title")}
787+
</div>
788+
</div>
789+
</div>
790+
</div>
791+
);
792+
}
793+
760794
return (
761795
<>
762796
<div
@@ -770,32 +804,6 @@ function DataConfig({ isActive }: DataConfigProps) {
770804
onDragLeave={handleDragLeave}
771805
onDrop={handleDrop}
772806
>
773-
{showEmbeddingWarning && (
774-
<div className="absolute inset-0 bg-gray-500/45 z-40" />
775-
)}
776-
<Modal
777-
open={showEmbeddingWarning && !!contentRef.current}
778-
title={null}
779-
footer={null}
780-
closable={false}
781-
maskClosable={false}
782-
mask={false}
783-
centered
784-
getContainer={() => contentRef.current || document.body}
785-
styles={{ body: { padding: 0 } }}
786-
rootClassName="kb-embedding-warning"
787-
>
788-
<div className="py-2">
789-
<div className="flex items-center">
790-
<WarningFilled className="text-yellow-500 mt-1 mr-2 text-3xl" />
791-
<div className="ml-3 mt-2">
792-
<div className="text-base text-gray-800 font-semibold">
793-
{t("embedding.knowledgeBaseDisabledWarningModal.title")}
794-
</div>
795-
</div>
796-
</div>
797-
</div>
798-
</Modal>
799807
<Modal
800808
open={showAutoDeselectModal}
801809
title={t("embedding.knowledgeBaseAutoDeselectModal.title")}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,13 @@ export const ModelAddDialog = ({
10721072
</div>
10731073
<div className="mt-2 ml-6 flex items-center">
10741074
<span>{t("model.dialog.label.currentlySupported")}</span>
1075+
<Tooltip title="ModelEngine">
1076+
<img
1077+
src="/modelengine-logo.png"
1078+
alt="ModelEngine"
1079+
className="h-4 ml-1.5"
1080+
/>
1081+
</Tooltip>
10751082
{form.isBatchImport && (
10761083
<Tooltip title="SiliconFlow">
10771084
<img

0 commit comments

Comments
Β (0)