From ae96e636e9cc2fe791f15a7171ca68e7202ea010 Mon Sep 17 00:00:00 2001 From: qinling0210 <88864212+qinling0210@users.noreply.github.com> Date: Thu, 9 Jul 2026 11:38:55 +0800 Subject: [PATCH] Handle searching dataset without embedding model (#16742) ### Summary Handle searching dataset without embedding model In this PR, Searching datasets with different embedding models or searching dataset with/without embedding models are not allowed. We will improve the behavior later. --- api/apps/restful_apis/chat_api.py | 10 +-- api/apps/services/dataset_api_service.py | 20 +++-- api/db/services/dialog_service.py | 18 ++--- api/db/services/knowledgebase_service.py | 23 ++++++ internal/handler/tenant.go | 4 +- internal/router/router.go | 12 +-- internal/service/chat.go | 29 +++----- internal/service/chat_pipeline.go | 45 ++++++------ internal/service/dataset.go | 73 ++++++++++++------- test/testcases/restful_api/test_chats.py | 11 +-- .../test_user_tenant_routes_unit.py | 1 + .../test_chat_sdk_routes_unit.py | 1 + .../test_session_sdk_routes_unit.py | 1 + .../api/apps/services/test_delete_datasets.py | 1 + 14 files changed, 142 insertions(+), 107 deletions(-) diff --git a/api/apps/restful_apis/chat_api.py b/api/apps/restful_apis/chat_api.py index d9a613f38..46e904e23 100644 --- a/api/apps/restful_apis/chat_api.py +++ b/api/apps/restful_apis/chat_api.py @@ -27,11 +27,11 @@ from quart import Response, request from api.apps import current_user, login_required from api.apps.restful_apis._generation_params import merge_generation_config, pop_generation_config -from api.db.joint_services.tenant_model_service import get_tenant_default_model_by_type, get_model_config_from_provider_instance, get_api_key, split_model_name +from api.db.joint_services.tenant_model_service import get_tenant_default_model_by_type, get_model_config_from_provider_instance, get_api_key from api.db.services.chunk_feedback_service import ChunkFeedbackService from api.db.services.conversation_service import ConversationService, structure_answer from api.db.services.dialog_service import DialogService, async_chat, gen_mindmap -from api.db.services.knowledgebase_service import KnowledgebaseService +from api.db.services.knowledgebase_service import KnowledgebaseService, validate_dataset_embedding_models from api.db.services.llm_service import LLMBundle from api.db.services.search_service import SearchService from api.db.services.user_service import TenantService, UserTenantService @@ -337,9 +337,9 @@ async def _validate_dataset_ids(dataset_ids, tenant_id): return f"The dataset {dataset_id} doesn't own parsed file" kbs.append(kb) - embd_ids = [split_model_name(kb.embd_id)[0] for kb in kbs] - if len(set(embd_ids)) > 1: - return f"Datasets use different embedding models: {[kb.embd_id for kb in kbs]}" + err = validate_dataset_embedding_models(kbs) + if err: + return err return normalized_ids diff --git a/api/apps/services/dataset_api_service.py b/api/apps/services/dataset_api_service.py index 5932606c2..4a0d5d9a0 100644 --- a/api/apps/services/dataset_api_service.py +++ b/api/apps/services/dataset_api_service.py @@ -25,7 +25,7 @@ from api.db.db_models import File from api.db.services.document_service import DocumentService, queue_raptor_o_graphrag_tasks from api.db.services.file2document_service import File2DocumentService from api.db.services.file_service import FileService -from api.db.services.knowledgebase_service import KnowledgebaseService +from api.db.services.knowledgebase_service import KnowledgebaseService, validate_dataset_embedding_models from api.db.services.connector_service import Connector2KbService from api.db.services.task_service import GRAPH_RAPTOR_FAKE_DOC_ID, TaskService from api.db.services.user_service import TenantService, UserService, UserTenantService @@ -1167,8 +1167,7 @@ def check_embedding(dataset_id: str, tenant_id: str, req: dict): except Exception as e: if "not_found_exception" in repr(e) or "index_not_found_exception" in repr(e): logging.info( - "sample_random_chunks_with_vectors: index %s not yet created for tenant %s; " - "returning empty sample set", + "sample_random_chunks_with_vectors: index %s not yet created for tenant %s; returning empty sample set", index_nm, tenant_id, ) @@ -1327,7 +1326,7 @@ async def search_datasets(tenant_id: str, req: dict): :param req: search request containing dataset_ids and other params :return: (success, result) or (success, error_message) """ - from api.db.joint_services.tenant_model_service import get_tenant_default_model_by_type, split_model_name + from api.db.joint_services.tenant_model_service import get_tenant_default_model_by_type from api.db.services.doc_metadata_service import DocMetadataService from api.db.services.llm_service import LLMBundle from api.db.services.search_service import SearchService @@ -1365,10 +1364,9 @@ async def search_datasets(tenant_id: str, req: dict): if not kbs: return False, "Datasets not found!" - # All datasets must use the same embedding model - embd_nms = list(set([split_model_name(kb.embd_id)[0] for kb in kbs])) - if len(embd_nms) != 1: - return False, "Datasets use different embedding models." + err = validate_dataset_embedding_models(kbs) + if err: + return False, err if doc_ids is not None and not isinstance(doc_ids, list): return False, "`doc_ids` should be a list" @@ -1437,11 +1435,11 @@ async def search_datasets(tenant_id: str, req: dict): _question = question if langs: _question = await cross_languages(kb.tenant_id, None, _question, langs) + + embd_mdl = None if kb.embd_id: embd_model_config = get_model_config_from_provider_instance(kb.tenant_id, LLMType.EMBEDDING, kb.embd_id) - else: - embd_model_config = get_tenant_default_model_by_type(kb.tenant_id, LLMType.EMBEDDING) - embd_mdl = LLMBundle(kb.tenant_id, embd_model_config) + embd_mdl = LLMBundle(kb.tenant_id, embd_model_config) rerank_mdl = None rerank_id = search_config.get("rerank_id") or req.get("rerank_id") diff --git a/api/db/services/dialog_service.py b/api/db/services/dialog_service.py index f27c89810..fb7ff2165 100644 --- a/api/db/services/dialog_service.py +++ b/api/db/services/dialog_service.py @@ -31,7 +31,7 @@ from common.constants import LLMType, ParserType, StatusEnum from api.db.db_models import DB, Dialog from api.db.services.common_service import CommonService from api.db.services.doc_metadata_service import DocMetadataService -from api.db.services.knowledgebase_service import KnowledgebaseService +from api.db.services.knowledgebase_service import KnowledgebaseService, validate_dataset_embedding_models from api.db.services.langfuse_service import TenantLangfuseService from api.db.services.llm_service import LLMBundle from common.metadata_utils import apply_meta_data_filter @@ -358,16 +358,16 @@ async def async_chat_solo(dialog, messages, stream=True, session_id=None): def get_models(dialog, trace_context=None, langfuse_session_id=None): embd_mdl, chat_mdl, rerank_mdl, tts_mdl = None, None, None, None kbs = KnowledgebaseService.get_by_ids(dialog.kb_ids) - embedding_list = list(set([kb.embd_id for kb in kbs])) - if len(embedding_list) > 1: - raise Exception("**ERROR**: Knowledge bases use different embedding models.") + err = validate_dataset_embedding_models(kbs) + if err: + raise Exception(err) - if embedding_list: + if kbs and kbs[0].embd_id: embd_owner_tenant_id = kbs[0].tenant_id - embd_model_config = get_model_config_from_provider_instance(embd_owner_tenant_id, LLMType.EMBEDDING, embedding_list[0]) + embd_model_config = get_model_config_from_provider_instance(embd_owner_tenant_id, LLMType.EMBEDDING, kbs[0].embd_id) embd_mdl = LLMBundle(embd_owner_tenant_id, embd_model_config, trace_context=trace_context, langfuse_session_id=langfuse_session_id) if not embd_mdl: - raise LookupError("Embedding model(%s) not found" % embedding_list[0]) + raise LookupError("Embedding model(%s) not found" % kbs[0].embd_id) if dialog.llm_id: if dialog.tenant_llm_id: @@ -721,8 +721,8 @@ async def async_chat(dialog, messages, stream=True, **kwargs): prompt_config, partial( retriever.retrieval, - embd_mdl = embd_mdl, - tenant_ids = tenant_ids, + embd_mdl=embd_mdl, + tenant_ids=tenant_ids, kb_ids=dialog.kb_ids, page=1, page_size=dialog.top_n, diff --git a/api/db/services/knowledgebase_service.py b/api/db/services/knowledgebase_service.py index 779669f5c..c05d46466 100644 --- a/api/db/services/knowledgebase_service.py +++ b/api/db/services/knowledgebase_service.py @@ -29,6 +29,29 @@ from api.constants import DATASET_NAME_LIMIT from api.utils.api_utils import get_parser_config, get_data_error_result +def _base_model_name(embd_id: str) -> str: + """Return the base model name by stripping provider/instance suffix from an embd_id.""" + parts = embd_id.rsplit("@", 2) + return parts[0] + + +def validate_dataset_embedding_models(kbs): + """Validate that all given datasets use the same embedding model (or all use none). + + Returns an error message string on failure, or ``None`` on success. + """ + # Either all datasets have an embedding model, or none do. Mixing is not allowed. + embd_ids = [kb.embd_id for kb in kbs if kb.embd_id] + has_embd = len(embd_ids) > 0 + if has_embd and len(embd_ids) != len(kbs): + return "Cannot search across datasets where some have embedding models and others do not." + if has_embd: + embd_nms = list({_base_model_name(eid) for eid in embd_ids}) + if len(embd_nms) > 1: + return f"Datasets use different embedding models: {[kb.embd_id for kb in kbs]}" + return None + + class KnowledgebaseService(CommonService): """Service class for managing dataset operations. diff --git a/internal/handler/tenant.go b/internal/handler/tenant.go index e422d897d..ae7a798b8 100644 --- a/internal/handler/tenant.go +++ b/internal/handler/tenant.go @@ -331,7 +331,7 @@ type InsertChunksFromFileRequest struct { // @Security ApiKeyAuth // @Param request body InsertChunksFromFileRequest true "insert chunks request" // @Success 200 {object} map[string]interface{} -// @Router /v1/tenant/insert_chunks_from_file [post] +// @Router /v1/tenant/dev_insert_chunks_from_file [post] func (h *TenantHandler) InsertChunksFromFile(c *gin.Context) { _, errorCode, errorMessage := GetUser(c) if errorCode != common.CodeSuccess { @@ -409,7 +409,7 @@ type InsertMetadataFromFileRequest struct { // @Security ApiKeyAuth // @Param request body InsertMetadataFromFileRequest true "insert metadata request" // @Success 200 {object} map[string]interface{} -// @Router /v1/tenant/insert_metadata_from_file [post] +// @Router /v1/tenant/dev_insert_metadata_from_file [post] func (h *TenantHandler) InsertMetadataFromFile(c *gin.Context) { user, errorCode, errorMessage := GetUser(c) if errorCode != common.CodeSuccess { diff --git a/internal/router/router.go b/internal/router/router.go index 13597e5e2..636e6629e 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -275,12 +275,12 @@ func (r *Router) Setup(engine *gin.Engine) { tenant := v1.Group("/tenant") { tenant.GET("/list", r.tenantHandler.TenantList) - tenant.POST("/chunk_store", r.tenantHandler.CreateChunkStore) // Internal API only for GO - tenant.DELETE("/chunk_store", r.tenantHandler.DeleteChunkStore) // Internal API only for GO - tenant.POST("/metadata_store", r.tenantHandler.CreateMetadataStore) // Internal API only for GO - tenant.DELETE("/metadata_store", r.tenantHandler.DeleteMetadataStore) // Internal API only for GO - tenant.POST("/insert_chunks_from_file", r.tenantHandler.InsertChunksFromFile) // Internal API only for GO - tenant.POST("/insert_metadata_from_file", r.tenantHandler.InsertMetadataFromFile) // Internal API only for GO + tenant.POST("/chunk_store", r.tenantHandler.CreateChunkStore) // Internal API only for GO + tenant.DELETE("/chunk_store", r.tenantHandler.DeleteChunkStore) // Internal API only for GO + tenant.POST("/metadata_store", r.tenantHandler.CreateMetadataStore) // Internal API only for GO + tenant.DELETE("/metadata_store", r.tenantHandler.DeleteMetadataStore) // Internal API only for GO + tenant.POST("/dev_insert_chunks_from_file", r.tenantHandler.InsertChunksFromFile) // Internal API only for GO + tenant.POST("/dev_insert_metadata_from_file", r.tenantHandler.InsertMetadataFromFile) // Internal API only for GO } // Document routes diff --git a/internal/service/chat.go b/internal/service/chat.go index f8ab3cd9f..e6ced41b1 100644 --- a/internal/service/chat.go +++ b/internal/service/chat.go @@ -289,12 +289,8 @@ func (s *ChatService) validateCreateDatasetIDs(value interface{}, tenantID strin kbs = append(kbs, kb) } - embedIDs := make(map[string]struct{}, len(kbs)) - for _, kb := range kbs { - embedIDs[s.splitModelNameAndFactory(kb.EmbdID)] = struct{}{} - } - if len(embedIDs) > 1 { - return nil, fmt.Errorf("Datasets use different embedding models: %v", getEmbdIDs(kbs)) + if err := validateDatasetEmbeddingModels(kbs); err != nil { + return nil, err } return normalizedIDs, nil } @@ -645,24 +641,21 @@ const ( pyDefaultEmptyResponse = "Sorry! No relevant content was found in the knowledge base!" ) -// splitModelNameAndFactory extracts the base model name (removes vendor suffix) +// splitModelNameAndFactory extracts the base model name by stripping +// provider and instance suffixes, matching Python's rsplit("@", 2)[0]. func (s *ChatService) splitModelNameAndFactory(embdID string) string { - // Remove vendor suffix (e.g., "model@openai" -> "model") if idx := strings.LastIndex(embdID, "@"); idx > 0 { - return embdID[:idx] + // Strip the provider segment. + base := embdID[:idx] + // Strip the instance segment (second-to-last @). + if idx2 := strings.LastIndex(base, "@"); idx2 > 0 { + return base[:idx2] + } + return base } return embdID } -// getEmbdIDs extracts embedding IDs from knowledge bases -func getEmbdIDs(kbs []*entity.Knowledgebase) []string { - ids := make([]string, len(kbs)) - for i, kb := range kbs { - ids[i] = kb.EmbdID - } - return ids -} - func (s *ChatService) getOwnedValidChat(userID, chatID string) (*entity.Chat, error) { chat, err := s.chatDAO.GetByIDAndStatus(chatID, string(entity.StatusValid)) if err != nil { diff --git a/internal/service/chat_pipeline.go b/internal/service/chat_pipeline.go index 4f9d539ce..599ff819d 100644 --- a/internal/service/chat_pipeline.go +++ b/internal/service/chat_pipeline.go @@ -245,7 +245,14 @@ func (s *ChatPipelineService) AsyncChat( // === Phase 4: Bind Models (embedding, rerank, chat, TTS) + ToolCall === common.Info("Phase 4: Bind Models (embedding, rerank, chat, TTS)") timer.Enter(common.PhaseBindModels) - kbs, embModel, rerankModel, chatModel, ttsModel := s.getModels(ctx, chat) + kbs, embModel, rerankModel, chatModel, ttsModel, err := s.getModels(ctx, chat) + if err != nil { + out <- AsyncChatResult{ + Answer: fmt.Sprintf("**ERROR**: %s", err.Error()), + Final: true, + } + return + } // Toolcall binding if toolcallSession, hasSession := kwargs["toolcall_session"]; hasSession && toolcallSession != nil { @@ -1918,6 +1925,7 @@ func (s *ChatPipelineService) getModels(ctx context.Context, chat *entity.Chat) *modelModule.RerankModel, *modelModule.ChatModel, *modelModule.ChatModel, // TTS model + error, ) { kbDAO := dao.NewKnowledgebaseDAO() @@ -1942,27 +1950,22 @@ func (s *ChatPipelineService) getModels(ctx context.Context, chat *entity.Chat) // Embedding model. var embModel *modelModule.EmbeddingModel if len(kbs) > 0 { - // All KBs must share the same embedding model. - embdIDs := make(map[string]bool) - for _, kb := range kbs { - if kb.EmbdID != "" { - embdIDs[kb.EmbdID] = true - } + if err := validateDatasetEmbeddingModels(kbs); err != nil { + return nil, nil, nil, nil, nil, err } - if len(embdIDs) > 1 { - // Multiple embedding models across KBs — error. - common.Warn("Knowledge bases use different embedding models") - } - if len(embdIDs) == 1 { - for embdID := range embdIDs { - embdTenantID := kbs[0].TenantID - driver, modelName, apiConfig, maxTokens, err := s.ModelProviderSvc.GetModelConfigFromProviderInstance( - embdTenantID, entity.ModelTypeEmbedding, embdID, - ) - if err == nil { - embModel = modelModule.NewEmbeddingModel(driver, &modelName, apiConfig, maxTokens) - } + if kbs[0].EmbdID != "" { + embdTenantID := kbs[0].TenantID + driver, modelName, apiConfig, maxTokens, err := s.ModelProviderSvc.GetModelConfigFromProviderInstance( + embdTenantID, entity.ModelTypeEmbedding, kbs[0].EmbdID, + ) + if err != nil { + common.Warn("Failed to get embedding model for chat retrieval", + zap.String("embdID", kbs[0].EmbdID), + zap.String("tenantID", embdTenantID), + zap.Error(err)) + return nil, nil, nil, nil, nil, fmt.Errorf("failed to get embedding model: %w", err) } + embModel = modelModule.NewEmbeddingModel(driver, &modelName, apiConfig, maxTokens) } } @@ -1997,7 +2000,7 @@ func (s *ChatPipelineService) getModels(ctx context.Context, chat *entity.Chat) } } - return kbs, embModel, rerankModel, chatModel, ttsModel + return kbs, embModel, rerankModel, chatModel, ttsModel, nil } // lastUserQuestion returns the content of the most recent user message in diff --git a/internal/service/dataset.go b/internal/service/dataset.go index 3b08147eb..ab5c74517 100644 --- a/internal/service/dataset.go +++ b/internal/service/dataset.go @@ -102,6 +102,47 @@ const ( graphPhaseCommunityDone = "community_done" ) +// validateDatasetEmbeddingModels checks that all given datasets use the same +// embedding model (or all use none). Returns an error on mismatch. +func validateDatasetEmbeddingModels(kbs []*entity.Knowledgebase) error { + embdIDs := make(map[string]struct{}) + hasEmbd := false + noEmbd := false + for _, kb := range kbs { + if kb.EmbdID != "" { + hasEmbd = true + baseName := kb.EmbdID + if idx := strings.LastIndex(kb.EmbdID, "@"); idx > 0 { + baseName = kb.EmbdID[:idx] + // Strip the second-to-last @-segment too (instance name), + // matching Python's _base_model_name which uses rsplit("@", 2). + if idx2 := strings.LastIndex(baseName, "@"); idx2 > 0 { + baseName = baseName[:idx2] + } + } + embdIDs[baseName] = struct{}{} + } else { + noEmbd = true + } + } + if hasEmbd && noEmbd { + return fmt.Errorf("Cannot search across datasets where some have embedding models and others do not.") + } + if len(embdIDs) > 1 { + return fmt.Errorf("Datasets use different embedding models: %v", getEmbdIDs(kbs)) + } + return nil +} + +// getEmbdIDs extracts embedding IDs from knowledge bases. +func getEmbdIDs(kbs []*entity.Knowledgebase) []string { + ids := make([]string, len(kbs)) + for i, kb := range kbs { + ids[i] = kb.EmbdID + } + return ids +} + // DatasetService implements the RESTful dataset APIs from dataset_api.py. type DatasetService struct { kbDAO *dao.KnowledgebaseDAO @@ -1872,13 +1913,8 @@ func (d *DatasetService) SearchDatasets(req *SearchDatasetsRequest, userID strin } // Check if all kbs have the same embedding model - if len(kbRecords) > 1 { - firstEmbdID := kbRecords[0].EmbdID - for i := 1; i < len(kbRecords); i++ { - if kbRecords[i].EmbdID != firstEmbdID { - return nil, fmt.Errorf("Datasets use different embedding models.") - } - } + if err := validateDatasetEmbeddingModels(kbRecords); err != nil { + return nil, err } // Override request fields with values from saved search config (if search_id is provided) @@ -2050,36 +2086,23 @@ func (d *DatasetService) SearchDatasets(req *SearchDatasetsRequest, userID strin return nil, fmt.Errorf("failed to get embedding model by embd_id: %w", embErr) } embeddingModel = models.NewEmbeddingModel(driver, &modelName, apiConfig, maxTokens) - } else { - driver, modelName, apiConfig, maxTokens, err := modelProviderSvc.GetTenantDefaultModelByType(tenantIDs[0], entity.ModelTypeEmbedding) - if err != nil { - return nil, fmt.Errorf("failed to get tenant default embedding model: %w", err) - } - embeddingModel = models.NewEmbeddingModel(driver, &modelName, apiConfig, maxTokens) + common.Info("Fetched embedding model for retrieval", + zap.String("tenantID", tenantIDs[0]), + zap.String("modelName", modelName)) + } - modelNameStr := "" - if embeddingModel.ModelName != nil { - modelNameStr = *embeddingModel.ModelName - } - common.Info("Fetched embedding model for retrieval", - zap.String("tenantID", tenantIDs[0]), - zap.String("modelName", modelNameStr)) // Get rerank model if rerankID is specified var rerankModel *models.RerankModel - if rerankID != "" { driver, modelName, apiConfig, _, rErr := modelProviderSvc.GetModelConfigFromProviderInstance(tenantIDs[0], entity.ModelTypeRerank, rerankID) if rErr != nil { return nil, fmt.Errorf("failed to get rerank model by rerank_id: %w", rErr) } rerankModel = models.NewRerankModel(driver, &modelName, apiConfig) - } - - if rerankModel != nil { common.Info("Fetched rerank model", zap.String("tenantID", tenantIDs[0]), - zap.String("modelName", *rerankModel.ModelName)) + zap.String("modelName", modelName)) } retrievalReq := &nlp.RetrievalRequest{ diff --git a/test/testcases/restful_api/test_chats.py b/test/testcases/restful_api/test_chats.py index 28f213ac7..d39df0c06 100644 --- a/test/testcases/restful_api/test_chats.py +++ b/test/testcases/restful_api/test_chats.py @@ -751,6 +751,7 @@ def _load_chat_routes_unit_module(monkeypatch): return False, None kb_service_mod.KnowledgebaseService = _StubKnowledgebaseService + kb_service_mod.validate_dataset_embedding_models = lambda _kbs: None monkeypatch.setitem(sys.modules, "api.db.services.knowledgebase_service", kb_service_mod) llm_service_mod = ModuleType("api.db.services.llm_service") @@ -1146,13 +1147,6 @@ def test_chat_create_accepts_provider_scoped_rerank_id_unit(monkeypatch): monkeypatch.setattr(module.KnowledgebaseService, "query", lambda **_kwargs: [_DummyKB()]) monkeypatch.setattr(module.KnowledgebaseService, "get_by_id", lambda _id: (True, _DummyKB())) - def _split_model_name_and_factory(model_name): - return {"glm-4@ZHIPU-AI": ("glm-4", "default", "ZHIPU-AI"), "glm-4@CI@ZHIPU-AI": ("glm-4", "CI", "ZHIPU-AI"), "custom-reranker@OpenAI": ("custom-reranker", "default", "OpenAI")}.get( - model_name, (model_name, None) - ) - - monkeypatch.setattr(module, "split_model_name", _split_model_name_and_factory) - def _get_model_config_from_provider_instance(**kwargs): query_calls.append(kwargs) return {} @@ -1225,7 +1219,6 @@ def test_chat_create_uses_direct_chat_fields_unit(monkeypatch): monkeypatch.setattr(module.KnowledgebaseService, "accessible", lambda **_kwargs: [SimpleNamespace(id="kb-1")]) monkeypatch.setattr(module.KnowledgebaseService, "query", lambda **_kwargs: [_DummyKB()]) monkeypatch.setattr(module.KnowledgebaseService, "get_by_id", lambda _id: (True, _DummyKB())) - monkeypatch.setattr(module, "split_model_name", lambda model: (model.split("@")[0], "default", "factory")) def _save(**kwargs): saved.update(kwargs) @@ -1380,7 +1373,6 @@ def test_patch_chat_drops_response_only_fields_before_update_unit(monkeypatch): monkeypatch.setattr(module.TenantService, "get_by_id", lambda _tid: (True, SimpleNamespace(llm_id="glm-4"))) monkeypatch.setattr(module.KnowledgebaseService, "accessible", lambda **_kwargs: [SimpleNamespace(id="kb-1")]) monkeypatch.setattr(module.KnowledgebaseService, "query", lambda **_kwargs: [_DummyKB()]) - monkeypatch.setattr(module, "split_model_name", lambda model: (model.split("@")[0], "default", "factory")) monkeypatch.setattr(module, "get_api_key", lambda *args, **kwargs: SimpleNamespace(id=1)) def _update(_chat_id, req): @@ -1450,7 +1442,6 @@ def test_update_chat_allows_knowledge_placeholder_without_sources_unit(monkeypat monkeypatch.setattr(module.DialogService, "query", lambda **_kwargs: [SimpleNamespace(id="chat-1")]) monkeypatch.setattr(module.DialogService, "get_by_id", lambda _id: (True, _DummyDialogRecord(existing))) monkeypatch.setattr(module.TenantService, "get_by_id", lambda _tid: (True, SimpleNamespace(llm_id="glm-4"))) - monkeypatch.setattr(module, "split_model_name", lambda model: (model.split("@")[0], "default", "factory")) updated = {} def _update(_chat_id, payload): diff --git a/test/testcases/restful_api/test_user_tenant_routes_unit.py b/test/testcases/restful_api/test_user_tenant_routes_unit.py index 5b24f88a3..13be31832 100644 --- a/test/testcases/restful_api/test_user_tenant_routes_unit.py +++ b/test/testcases/restful_api/test_user_tenant_routes_unit.py @@ -1497,6 +1497,7 @@ def _load_chat_routes_unit_module(monkeypatch): "get_by_id": staticmethod(lambda _id: (True, _KB())), }, ) + kb_service_mod.validate_dataset_embedding_models = lambda _kbs: None monkeypatch.setitem(sys.modules, "api.db.services.knowledgebase_service", kb_service_mod) tenant_model_provider_mod = ModuleType("api.db.joint_services.tenant_model_service") diff --git a/test/testcases/test_http_api/test_chat_assistant_management/test_chat_sdk_routes_unit.py b/test/testcases/test_http_api/test_chat_assistant_management/test_chat_sdk_routes_unit.py index fea449b48..c7eba8229 100644 --- a/test/testcases/test_http_api/test_chat_assistant_management/test_chat_sdk_routes_unit.py +++ b/test/testcases/test_http_api/test_chat_assistant_management/test_chat_sdk_routes_unit.py @@ -330,6 +330,7 @@ def _load_chat_module(monkeypatch): return False, None kb_service_mod.KnowledgebaseService = _StubKnowledgebaseService + kb_service_mod.validate_dataset_embedding_models = lambda _kbs: None monkeypatch.setitem(sys.modules, "api.db.services.knowledgebase_service", kb_service_mod) tenant_llm_service_mod = ModuleType("api.db.services.tenant_llm_service") diff --git a/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py b/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py index bca801dfd..7530b893f 100644 --- a/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py +++ b/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py @@ -2301,6 +2301,7 @@ def _load_chat_api_module(monkeypatch): kb_svc_mod = ModuleType("api.db.services.knowledgebase_service") kb_svc_mod.KnowledgebaseService = SimpleNamespace(query=lambda **_k: [], accessible=lambda **_k: True) + kb_svc_mod.validate_dataset_embedding_models = lambda _kbs: None monkeypatch.setitem(sys.modules, "api.db.services.knowledgebase_service", kb_svc_mod) class _FakeLLMBundle: diff --git a/test/unit_test/api/apps/services/test_delete_datasets.py b/test/unit_test/api/apps/services/test_delete_datasets.py index a0badec5f..bc1b04d4e 100644 --- a/test/unit_test/api/apps/services/test_delete_datasets.py +++ b/test/unit_test/api/apps/services/test_delete_datasets.py @@ -74,6 +74,7 @@ def _load_delete_datasets_module(monkeypatch, *, f2d_rows, file_filter_delete): delete_by_id=lambda kb_id: True, query=lambda **kwargs: [], ), + validate_dataset_embedding_models=lambda kbs: None, ) _stub( monkeypatch,