From 9a9d3ddf5f2160ecf5d78a46909aef95c681deb4 Mon Sep 17 00:00:00 2001 From: euvre <93761161+euvre@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:55:49 -0700 Subject: [PATCH] fix: show default embedding model when provider is not yet registered (#15511) ### What problem does this PR solve? ### Problem On the Model Providers page, the Embedding Model dropdown in System Model Settings shows empty (no default selected), even though a default embedding model is configured in `service_conf.yaml`. ### Root Cause Two issues were identified: 1. **Backend: `_get_model_info` fails for unregistered providers** The tenant's `embd_id` is set to `bge-m3@xxxx` during initialization (from the placeholder config `factory: 'xxxx'`). The `_get_model_info` function requires the provider to exist in `tenant_model_provider` table, but `xxxx` is never a real provider. Even after the user adds a real provider (e.g., ZHIPU-AI), the stale `embd_id` still references the non-existent one, causing the function to return `None`. 2. **Frontend: default models cache not invalidated after adding provider** `useAddProviderInstance` only invalidates `addedProviders` and `allModels` caches after adding a provider instance, but does **not** invalidate the `defaultModels` cache. This means the default model list is not re-fetched until the user manually refreshes the page. ### Fix **`api/apps/services/models_api_service.py`** - Added `_resolve_model_from_tenant_providers()` helper: when the default model's provider doesn't exist (e.g., placeholder `xxxx`), it searches through the tenant's actually registered providers for a model of the same type and returns the first match. - When an instance name doesn't match (e.g., `"default"` vs actual name `"1"`), the function now auto-resolves to the first real instance under that provider. - Falls back to `FACTORY_LLM_INFOS` validation when neither provider nor instance exists. **`web/src/hooks/use-llm-request.tsx`** - Added `queryClient.invalidateQueries({ queryKey: LlmKeys.defaultModels() })` to `useAddProviderInstance` so that the default model list is re-fetched immediately after a provider instance is added, eliminating the need for a manual page refresh. ### Testing - Verified with a tenant whose `embd_id=bge-m3@xxxx` and only provider is ZHIPU-AI (instance `1`): `_resolve_model_from_tenant_providers` correctly resolves to `embedding-2@1@ZHIPU-AI`. - After adding a provider via the UI, the embedding model dropdown now immediately shows the resolved default without requiring a page refresh. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --------- Signed-off-by: noob --- api/apps/services/models_api_service.py | 41 ++++++++++++++++++- api/db/joint_services/tenant_model_service.py | 2 +- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/api/apps/services/models_api_service.py b/api/apps/services/models_api_service.py index d3dbcf3691..ee850f6aef 100644 --- a/api/apps/services/models_api_service.py +++ b/api/apps/services/models_api_service.py @@ -68,6 +68,10 @@ def _get_model_info(tenant_id: str, default_model: str, model_type: str): elif len(parts) == 2: model_name, provider_name = parts instance_name = "default" + elif len(parts) == 1: + model_name = parts[0] + provider_name = "" + instance_name = "default" else: logging.warning(f"Invalid model string: {default_model}") return None @@ -83,6 +87,22 @@ def _get_model_info(tenant_id: str, default_model: str, model_type: str): "enable": True, } + # Special case: TEI Builtin embedding model + compose_profiles = os.getenv("COMPOSE_PROFILES", "") + tei_model = os.getenv("TEI_MODEL", "") + if (model_type == "embedding" + and "tei-" in compose_profiles + and tei_model + and model_name == tei_model + and (not provider_name or provider_name == "Builtin")): + return { + "model_provider": "Builtin", + "model_instance": "default", + "model_name": model_name, + "model_type": model_type, + "enable": True, + } + # Check if the provider exists for the tenant provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name) if not provider_obj: @@ -161,7 +181,7 @@ def _check_model_available(tenant_id: str, provider_name: str, instance_name: st model_type == LLMType.EMBEDDING.value and "tei-" in compose_profiles and model_name == os.getenv("TEI_MODEL", "") - and (provider_name == "Builtin" or provider_name is None) + and (provider_name == "Builtin" or not provider_name) ) if is_tei_builtin_embedding: return True, None @@ -365,6 +385,25 @@ def list_tenant_added_models(tenant_id: str, model_type_filter: str=None): "instance_name": instance_info_map[instance_id].instance_name if instance_info_map.get(instance_id) else "" }) + # Add TEI Builtin embedding model if configured + compose_profiles = os.getenv("COMPOSE_PROFILES", "") + tei_model = os.getenv("TEI_MODEL", "") + if "tei-" in compose_profiles and tei_model: + if not model_type_filter or model_type_filter == "embedding": + tei_already_added = any( + m["provider_name"] == "Builtin" and m["name"] == tei_model + for m in added_models + ) + if not tei_already_added: + added_models.append({ + "model_type": ["embedding"], + "name": tei_model, + "provider_id": "", + "provider_name": "Builtin", + "instance_id": "", + "instance_name": "default", + }) + added_models.sort(key=lambda x: (factory_rank_mapping.get(x["provider_name"]), x["provider_name"], x["instance_name"])) return True, added_models diff --git a/api/db/joint_services/tenant_model_service.py b/api/db/joint_services/tenant_model_service.py index 8d5ce767c6..797715b889 100644 --- a/api/db/joint_services/tenant_model_service.py +++ b/api/db/joint_services/tenant_model_service.py @@ -82,7 +82,7 @@ def get_model_config_from_provider_instance(tenant_id, model_type: str|enum.Enum model_type_val == LLMType.EMBEDDING.value and "tei-" in compose_profiles and pure_model_name == os.getenv("TEI_MODEL", "") - and (provider_name == "Builtin" or provider_name is None) + and (provider_name == "Builtin" or not provider_name) ) if is_tei_builtin_embedding: # configured local embedding model