diff --git a/apps/application/chat_pipeline/step/search_dataset_step/impl/base_search_dataset_step.py b/apps/application/chat_pipeline/step/search_dataset_step/impl/base_search_dataset_step.py index 9ecfbd44f..0073b53e3 100644 --- a/apps/application/chat_pipeline/step/search_dataset_step/impl/base_search_dataset_step.py +++ b/apps/application/chat_pipeline/step/search_dataset_step/impl/base_search_dataset_step.py @@ -40,6 +40,8 @@ def get_embedding_id(knowledge_id_list): _("The vector model of the associated knowledge base is inconsistent and the segmentation cannot be recalled.")) if len(knowledge_list) == 0: raise Exception(_("The knowledge base setting is wrong, please reset the knowledge base")) + if knowledge_list[0].embedding_model_id is None: + raise Exception(_("The knowledge base setting is wrong, please reset the knowledge base")) return knowledge_list[0].embedding_model_id diff --git a/apps/application/flow/step_node/search_knowledge_node/impl/base_search_knowledge_node.py b/apps/application/flow/step_node/search_knowledge_node/impl/base_search_knowledge_node.py index b9ae92618..5621f0351 100644 --- a/apps/application/flow/step_node/search_knowledge_node/impl/base_search_knowledge_node.py +++ b/apps/application/flow/step_node/search_knowledge_node/impl/base_search_knowledge_node.py @@ -30,6 +30,8 @@ def get_embedding_id(dataset_id_list): raise Exception("关联知识库的向量模型不一致,无法召回分段。") if len(dataset_list) == 0: raise Exception("知识库设置错误,请重新设置知识库") + if dataset_list[0].embedding_model_id is None: + raise Exception("Knowledge base setting error, please reset the knowledge base") return dataset_list[0].embedding_model_id diff --git a/apps/chat/serializers/chat_authentication.py b/apps/chat/serializers/chat_authentication.py index e2cccf3f4..7d9c18e74 100644 --- a/apps/chat/serializers/chat_authentication.py +++ b/apps/chat/serializers/chat_authentication.py @@ -13,7 +13,8 @@ from django.db.models import QuerySet from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from application.models import ApplicationAccessToken, ChatUserType, Application, ApplicationVersion +from application.models import ApplicationAccessToken, ChatUserType, Application, ApplicationVersion, \ + ApplicationKnowledgeMapping from application.models.application_api_key import ApplicationApiKey from application.serializers.application import ApplicationSerializerModel from common.auth.common import ChatUserToken, ChatAuthentication @@ -23,6 +24,7 @@ from common.constants.cache_version import Cache_Version from common.database_model_manage.database_model_manage import DatabaseModelManage from common.exception.app_exception import NotFound404, AppUnauthorizedFailed from common.utils.rsa_util import get_key_pair_by_sql +from knowledge.models import Knowledge, Problem class AnonymousAuthenticationSerializer(serializers.Serializer): @@ -173,6 +175,61 @@ class ApplicationProfileSerializer(serializers.Serializer): _v = getattr(application_version, version_field) setattr(application, app_field, _v) + @staticmethod + def get_work_flow_knowledge_id_list(work_flow): + knowledge_id_list = [] + knowledge_keys = { + 'knowledge_id', + 'knowledge_id_list', + 'dataset_id', + 'dataset_id_list', + 'all_knowledge_id_list', + } + + def collect(data): + if isinstance(data, dict): + for key, value in data.items(): + if key in knowledge_keys: + if isinstance(value, list): + knowledge_id_list.extend([str(item) for item in value if item]) + elif value: + knowledge_id_list.append(str(value)) + else: + collect(value) + elif isinstance(data, list): + for item in data: + collect(item) + + collect(work_flow or {}) + return list(dict.fromkeys(knowledge_id_list)) + + @staticmethod + def get_suggested_questions(application): + knowledge_id_list = [str(knowledge_id) for knowledge_id in QuerySet(ApplicationKnowledgeMapping).filter( + application_id=application.id + ).values_list('knowledge_id', flat=True)] + knowledge_id_list.extend(ApplicationProfileSerializer.get_work_flow_knowledge_id_list(application.work_flow)) + knowledge_id_list = list(dict.fromkeys(knowledge_id_list)) + knowledge_id_list = [str(knowledge_id) for knowledge_id in QuerySet(Knowledge).filter( + id__in=knowledge_id_list + ).values_list('id', flat=True)] + problem_query_set = QuerySet(Problem) + if len(knowledge_id_list) > 0: + problem_query_set = problem_query_set.filter(knowledge_id__in=knowledge_id_list) + else: + problem_query_set = problem_query_set.filter(knowledge__workspace_id=application.workspace_id) + problem_list = problem_query_set.exclude(content='').order_by( + '-hit_num', '-create_time' + ).values_list('content', flat=True) + result = [] + for content in problem_list: + content = (content or '').strip() + if content and content not in result: + result.append(content) + if len(result) >= 3: + break + return result + def profile(self, with_valid=True): if with_valid: self.is_valid() @@ -219,6 +276,7 @@ class ApplicationProfileSerializer(serializers.Serializer): 'show_user_avatar': application_setting.show_user_avatar, 'float_location': application_setting.float_location, 'chat_background': application_setting.chat_background} + suggested_questions = self.get_suggested_questions(application) base_node = [node for node in ((application.work_flow or {}).get('nodes', []) or []) if node.get('id') == 'base-node'] return {**ApplicationSerializerModel(application).data, @@ -232,6 +290,7 @@ class ApplicationProfileSerializer(serializers.Serializer): 'file_upload_enable': application.file_upload_enable, 'file_upload_setting': application.file_upload_setting, 'work_flow': {'nodes': base_node} if base_node else None, + 'suggested_questions': suggested_questions, 'show_source': application_access_token.show_source, 'show_exec': application_access_token.show_exec, 'language': application_access_token.language, diff --git a/apps/knowledge/serializers/common.py b/apps/knowledge/serializers/common.py index a76769cba..be796ea4a 100644 --- a/apps/knowledge/serializers/common.py +++ b/apps/knowledge/serializers/common.py @@ -119,6 +119,8 @@ def get_embedding_model_by_knowledge_id_list(knowledge_id_list: List): raise Exception(_('The knowledge base is inconsistent with the vector model')) if len(knowledge_list) == 0: raise Exception(_('Knowledge base setting error, please reset the knowledge base')) + if knowledge_list[0].embedding_model_id is None or knowledge_list[0].embedding_model is None: + raise Exception(_('Knowledge base setting error, please reset the knowledge base')) default_params = get_model_default_params(knowledge_list[0].embedding_model) @@ -130,6 +132,8 @@ def get_embedding_model_by_knowledge_id_list(knowledge_id_list: List): def get_embedding_model_by_knowledge_id(knowledge_id: str): knowledge = QuerySet(Knowledge).select_related('embedding_model').filter(id=knowledge_id).first() + if knowledge is None or knowledge.embedding_model_id is None or knowledge.embedding_model is None: + raise Exception(_('Knowledge base setting error, please reset the knowledge base')) default_params = get_model_default_params(knowledge.embedding_model) @@ -138,6 +142,8 @@ def get_embedding_model_by_knowledge_id(knowledge_id: str): def get_embedding_model_by_knowledge(knowledge): + if knowledge is None or knowledge.embedding_model_id is None or knowledge.embedding_model is None: + raise Exception(_('Knowledge base setting error, please reset the knowledge base')) default_params = get_model_default_params(knowledge.embedding_model) return ModelManage.get_model(str(knowledge.embedding_model_id), @@ -146,6 +152,8 @@ def get_embedding_model_by_knowledge(knowledge): def get_embedding_model_id_by_knowledge_id(knowledge_id): knowledge = QuerySet(Knowledge).select_related('embedding_model').filter(id=knowledge_id).first() + if knowledge is None or knowledge.embedding_model_id is None: + raise Exception(_('Knowledge base setting error, please reset the knowledge base')) return str(knowledge.embedding_model_id) @@ -155,6 +163,8 @@ def get_embedding_model_id_by_knowledge_id_list(knowledge_id_list: List): raise Exception(_('The knowledge base is inconsistent with the vector model')) if len(knowledge_list) == 0: raise Exception(_('Knowledge base setting error, please reset the knowledge base')) + if knowledge_list[0].embedding_model_id is None: + raise Exception(_('Knowledge base setting error, please reset the knowledge base')) return str(knowledge_list[0].embedding_model_id) diff --git a/apps/knowledge/serializers/document.py b/apps/knowledge/serializers/document.py index 005984711..16ab61773 100644 --- a/apps/knowledge/serializers/document.py +++ b/apps/knowledge/serializers/document.py @@ -1702,9 +1702,11 @@ class DocumentSerializers(serializers.Serializer): source_file.save(file_content) file.seek(0) - parsed_documents = DocumentSerializers.Split( + split_serializer = DocumentSerializers.Split( data={'workspace_id': self.data.get('workspace_id'), 'knowledge_id': self.data.get('knowledge_id')} - ).file_to_paragraph(file, None, None, 4096) + ) + split_serializer.is_valid(instance={'file': [file]}, raise_exception=True) + parsed_documents = split_serializer.file_to_paragraph(file, None, None, 4096) paragraphs = [] for parsed_document in parsed_documents: paragraphs.extend(parsed_document.get('content', [])) diff --git a/apps/models_provider/tools.py b/apps/models_provider/tools.py index 1d52e5c4b..e178bb1dc 100644 --- a/apps/models_provider/tools.py +++ b/apps/models_provider/tools.py @@ -47,6 +47,8 @@ def get_model(model, **kwargs): @param model: model 数据库Model实例对象 @return: 模型实例 """ + if model is None: + raise Exception(_("Model does not exist")) return get_model_(model.provider, model.model_type, model.model_name, model.credential, str(model.id), **kwargs) @@ -105,6 +107,8 @@ def is_valid_credential(provider, model_type, model_name, model_credential: Dict def get_model_by_id(_id, workspace_id): + if _id is None: + raise Exception(_("Model does not exist")) model = QuerySet(Model).filter(id=_id).first() # 归还链接到连接池 connection.close() @@ -116,6 +120,9 @@ def get_model_by_id(_id, workspace_id): return model def get_model_default_params(model): + if model is None: + raise Exception(_("Model does not exist")) + def convert_to_int(value): if isinstance(value, str): try: diff --git a/ui/src/components/ai-chat/component/prologue-content/index.vue b/ui/src/components/ai-chat/component/prologue-content/index.vue index 1a0054436..5e22433c6 100644 --- a/ui/src/components/ai-chat/component/prologue-content/index.vue +++ b/ui/src/components/ai-chat/component/prologue-content/index.vue @@ -43,11 +43,48 @@ const showUserAvatar = computed(() => { return props.application.show_user_avatar == undefined ? true : props.application.show_user_avatar }) +const applicationName = computed(() => { + return props.application?.name || props.application?.application_name || 'XXX' +}) + +const replaceApplicationName = (text: string) => { + return (text || '').replaceAll('XXX', applicationName.value) +} + +const defaultQuestions = computed(() => { + const source = replaceApplicationName(props.application?.prologue || '') + const questionList = Array.from(source.matchAll(/-\s+(.+)/g)) + .map((match) => match[1]?.trim()) + .filter((item) => item) + if (questionList.length) { + return questionList.slice(0, 3) + } + return [ + `${applicationName.value}主要功能有什么?`, + `${applicationName.value}如何收费?`, + '需要转人工服务', + ] +}) + +const suggestedQuestions = computed(() => { + const questions = props.application?.suggested_questions + return Array.isArray(questions) + ? questions.map((item) => `${item || ''}`.trim()).filter((item) => item).slice(0, 3) + : [] +}) + const toQuickQuestion = (match: string, offset: number, input: string) => { return `${match.replace('- ', '')}` } const prologue = computed(() => { - const temp = props.available ? props.application?.prologue : t('chat.tip.prologueMessage') + const temp = props.available + ? [ + `您好,我是${applicationName.value},您可以向我提出以下或其它问题。`, + ...(suggestedQuestions.value.length ? suggestedQuestions.value : defaultQuestions.value).map( + (item) => `${item}`, + ), + ].join('\n') + : t('chat.tip.prologueMessage') if (temp) { const tag_list = [ /[\d\D]*?<\/html_rander>/g, diff --git a/ui/src/components/markdown/MdRenderer.vue b/ui/src/components/markdown/MdRenderer.vue index be0fc7485..27d0bcdeb 100644 --- a/ui/src/components/markdown/MdRenderer.vue +++ b/ui/src/components/markdown/MdRenderer.vue @@ -5,15 +5,20 @@