diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml index 163b36eb5..602b51387 100644 --- a/.github/workflows/build-and-push.yml +++ b/.github/workflows/build-and-push.yml @@ -48,7 +48,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - ref: main + ref: ${{ github.ref_name }} - name: Prepare id: prepare run: | @@ -104,7 +104,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - ref: main + ref: ${{ github.ref_name }} - name: Prepare id: prepare run: | diff --git a/.github/workflows/typos_check.yml b/.github/workflows/typos_check.yml new file mode 100644 index 000000000..099b219d3 --- /dev/null +++ b/.github/workflows/typos_check.yml @@ -0,0 +1,13 @@ +name: Typos Check +on: [push, pull_request] + +jobs: + run: + name: Spell Check with Typos + runs-on: ubuntu-latest + steps: + - name: Checkout Actions Repository + uses: actions/checkout@v2 + + - name: Check spelling + uses: crate-ci/typos@master diff --git a/.gitignore b/.gitignore index 7e6be9bfb..c6d277f80 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,7 @@ share/python-wheels/ MANIFEST # PyInstaller -# Usually these files are written by a python script froms a template +# Usually these files are written by a python script forms a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec diff --git a/README.md b/README.md index 2cde66c3b..4c5021ad7 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ docker run -d --name=maxkb -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data 1pa - 前端:[Vue.js](https://cn.vuejs.org/) - 后端:[Python / Django](https://www.djangoproject.com/) -- Langchain:[Langchain](https://www.langchain.com/) +- LangChain:[LangChain](https://www.langchain.com/) - 向量数据库:[PostgreSQL / pgvector](https://www.postgresql.org/) - 大模型:Azure OpenAI、百度千帆大模型、[Ollama](https://github.com/ollama/ollama) @@ -59,6 +59,14 @@ docker run -d --name=maxkb -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data 1pa [![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/MaxKB&type=Date)](https://star-history.com/#1Panel-dev/MaxKB&Date) +## 我们的其他开源产品 + +- [JumpServer](https://github.com/jumpserver/jumpserver/) - 广受欢迎的开源堡垒机 +- [DataEase](https://github.com/dataease/dataease/) - 人人可用的开源数据可视化分析工具 +- [MeterSphere](https://github.com/metersphere/metersphere/) - 一站式开源自动化测试平台 +- [1Panel](https://github.com/1panel-dev/1panel/) - 现代化、开源的 Linux 服务器运维管理面板 +- [Halo](https://github.com/halo-dev/halo/) - 强大易用的开源建站工具 + ## License Copyright (c) 2014-2024 飞致云 FIT2CLOUD, All rights reserved. diff --git a/apps/application/serializers/application_serializers.py b/apps/application/serializers/application_serializers.py index 44ecce1f9..3b20a1b39 100644 --- a/apps/application/serializers/application_serializers.py +++ b/apps/application/serializers/application_serializers.py @@ -196,7 +196,7 @@ class ApplicationSerializer(serializers.Serializer): access_token = serializers.CharField(required=True, error_messages=ErrMessage.char("access_token")) def auth(self, request, with_valid=True): - token = request.META.get('HTTP_AUTHORIZATION', None) + token = request.META.get('HTTP_AUTHORIZATION') token_details = None try: # 校验token @@ -257,7 +257,7 @@ class ApplicationSerializer(serializers.Serializer): application_model = ApplicationSerializer.Create.to_application_model(user_id, application) dataset_id_list = application.get('dataset_id_list', []) application_dataset_mapping_model_list = [ - ApplicationSerializer.Create.to_application_dateset_mapping(application_model.id, dataset_id) for + ApplicationSerializer.Create.to_application_dataset_mapping(application_model.id, dataset_id) for dataset_id in dataset_id_list] # 插入应用 application_model.save() @@ -280,7 +280,7 @@ class ApplicationSerializer(serializers.Serializer): ) @staticmethod - def to_application_dateset_mapping(application_id: str, dataset_id: str): + def to_application_dataset_mapping(application_id: str, dataset_id: str): return ApplicationDatasetMapping(id=uuid.uuid1(), application_id=application_id, dataset_id=dataset_id) class HitTest(serializers.Serializer): diff --git a/apps/application/serializers/chat_message_serializers.py b/apps/application/serializers/chat_message_serializers.py index 3f0cf01cf..dbbffb2a2 100644 --- a/apps/application/serializers/chat_message_serializers.py +++ b/apps/application/serializers/chat_message_serializers.py @@ -178,12 +178,12 @@ class ChatMessageSerializer(serializers.Serializer): client_id = self.data.get('client_id') client_type = self.data.get('client_type') chat_info = self.is_valid(raise_exception=True) - pipline_manage_builder = PipelineManage.builder() + pipeline_manage_builder = PipelineManage.builder() # 如果开启了问题优化,则添加上问题优化步骤 if chat_info.application.problem_optimization: - pipline_manage_builder.append_step(BaseResetProblemStep) + pipeline_manage_builder.append_step(BaseResetProblemStep) # 构建流水线管理器 - pipline_message = (pipline_manage_builder.append_step(BaseSearchDatasetStep) + pipeline_message = (pipeline_manage_builder.append_step(BaseSearchDatasetStep) .append_step(BaseGenerateHumanMessageStep) .append_step(BaseChatStep) .build()) @@ -198,8 +198,8 @@ class ChatMessageSerializer(serializers.Serializer): params = chat_info.to_pipeline_manage_params(message, get_post_handler(chat_info), exclude_paragraph_id_list, client_id, client_type, stream) # 运行流水线作业 - pipline_message.run(params) - return pipline_message.context['chat_result'] + pipeline_message.run(params) + return pipeline_message.context['chat_result'] @staticmethod def re_open_chat(chat_id: str): diff --git a/apps/application/template/embed.js b/apps/application/template/embed.js index eff570e40..33545af47 100644 --- a/apps/application/template/embed.js +++ b/apps/application/template/embed.js @@ -247,7 +247,7 @@ function initMaxkbStyle(root){ #maxkb #maxkb-chat-container{ z-index:10000;position: relative; border-radius: 8px; - border: 1px solid var(--N300, #DEE0E3); + border: 1px solid #ffffff; background: linear-gradient(188deg, rgba(235, 241, 255, 0.20) 39.6%, rgba(231, 249, 255, 0.20) 94.3%), #EFF0F1; box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.10); position: fixed;bottom: 20px;right: 45px;overflow: hidden; diff --git a/apps/common/auth/authenticate.py b/apps/common/auth/authenticate.py index de1499e70..3d2a2258e 100644 --- a/apps/common/auth/authenticate.py +++ b/apps/common/auth/authenticate.py @@ -47,8 +47,7 @@ class TokenDetails: class TokenAuth(TokenAuthentication): # 重新 authenticate 方法,自定义认证规则 def authenticate(self, request): - auth = request.META.get('HTTP_AUTHORIZATION', None - ) + auth = request.META.get('HTTP_AUTHORIZATION') # 未认证 if auth is None: raise AppAuthenticationFailed(1003, '未登录,请先登录') diff --git a/apps/common/event/listener_manage.py b/apps/common/event/listener_manage.py index 93613a02a..98e22c821 100644 --- a/apps/common/event/listener_manage.py +++ b/apps/common/event/listener_manage.py @@ -215,7 +215,7 @@ class ListenerManagement: @staticmethod @poxy - def init_embedding_model(ags): + def init_embedding_model(ages): EmbeddingModel.get_embedding_model() def run(self): diff --git a/apps/common/froms/__init__.py b/apps/common/forms/__init__.py similarity index 100% rename from apps/common/froms/__init__.py rename to apps/common/forms/__init__.py diff --git a/apps/common/froms/array_object_card.py b/apps/common/forms/array_object_card.py similarity index 94% rename from apps/common/froms/array_object_card.py rename to apps/common/forms/array_object_card.py index 9cf8c9bf8..2dc71aaaf 100644 --- a/apps/common/froms/array_object_card.py +++ b/apps/common/forms/array_object_card.py @@ -8,7 +8,7 @@ """ from typing import Dict -from common.froms.base_field import BaseExecField, TriggerType +from common.forms.base_field import BaseExecField, TriggerType class ArrayCard(BaseExecField): diff --git a/apps/common/froms/base_field.py b/apps/common/forms/base_field.py similarity index 100% rename from apps/common/froms/base_field.py rename to apps/common/forms/base_field.py diff --git a/apps/common/froms/base_form.py b/apps/common/forms/base_form.py similarity index 92% rename from apps/common/froms/base_form.py rename to apps/common/forms/base_form.py index 49e4556fa..93984b8c6 100644 --- a/apps/common/froms/base_form.py +++ b/apps/common/forms/base_form.py @@ -6,7 +6,7 @@ @date:2023/11/1 16:04 @desc: """ -from common.froms import BaseField +from common.forms import BaseField class BaseForm: diff --git a/apps/common/froms/multi_select.py b/apps/common/forms/multi_select.py similarity index 95% rename from apps/common/froms/multi_select.py rename to apps/common/forms/multi_select.py index f3c854224..791c8e974 100644 --- a/apps/common/froms/multi_select.py +++ b/apps/common/forms/multi_select.py @@ -8,7 +8,7 @@ """ from typing import List, Dict -from common.froms.base_field import BaseExecField, TriggerType +from common.forms.base_field import BaseExecField, TriggerType class MultiSelect(BaseExecField): diff --git a/apps/common/froms/object_card.py b/apps/common/forms/object_card.py similarity index 94% rename from apps/common/froms/object_card.py rename to apps/common/forms/object_card.py index 0245ba296..ddb192ef9 100644 --- a/apps/common/froms/object_card.py +++ b/apps/common/forms/object_card.py @@ -8,7 +8,7 @@ """ from typing import Dict -from common.froms.base_field import BaseExecField, TriggerType +from common.forms.base_field import BaseExecField, TriggerType class ObjectCard(BaseExecField): diff --git a/apps/common/froms/password_input.py b/apps/common/forms/password_input.py similarity index 93% rename from apps/common/froms/password_input.py rename to apps/common/forms/password_input.py index 87a97e24d..e7c7923bb 100644 --- a/apps/common/froms/password_input.py +++ b/apps/common/forms/password_input.py @@ -8,7 +8,7 @@ """ from typing import Dict -from common.froms import BaseField, TriggerType +from common.forms import BaseField, TriggerType class PasswordInputField(BaseField): diff --git a/apps/common/froms/radio_button_field.py b/apps/common/forms/radio_button_field.py similarity index 94% rename from apps/common/froms/radio_button_field.py rename to apps/common/forms/radio_button_field.py index 5c7f2cf9a..aa6952303 100644 --- a/apps/common/froms/radio_button_field.py +++ b/apps/common/forms/radio_button_field.py @@ -8,7 +8,7 @@ """ from typing import List, Dict -from common.froms.base_field import BaseExecField, TriggerType +from common.forms.base_field import BaseExecField, TriggerType class Radio(BaseExecField): diff --git a/apps/common/froms/radio_card_field.py b/apps/common/forms/radio_card_field.py similarity index 94% rename from apps/common/froms/radio_card_field.py rename to apps/common/forms/radio_card_field.py index 07c57dcd9..b3579b84d 100644 --- a/apps/common/froms/radio_card_field.py +++ b/apps/common/forms/radio_card_field.py @@ -8,7 +8,7 @@ """ from typing import List, Dict -from common.froms.base_field import BaseExecField, TriggerType +from common.forms.base_field import BaseExecField, TriggerType class Radio(BaseExecField): diff --git a/apps/common/froms/radio_field.py b/apps/common/forms/radio_field.py similarity index 94% rename from apps/common/froms/radio_field.py rename to apps/common/forms/radio_field.py index b8fcbd9b1..94a016d9d 100644 --- a/apps/common/froms/radio_field.py +++ b/apps/common/forms/radio_field.py @@ -8,7 +8,7 @@ """ from typing import List, Dict -from common.froms.base_field import BaseExecField, TriggerType +from common.forms.base_field import BaseExecField, TriggerType class Radio(BaseExecField): diff --git a/apps/common/froms/single_select_field.py b/apps/common/forms/single_select_field.py similarity index 95% rename from apps/common/froms/single_select_field.py rename to apps/common/forms/single_select_field.py index f0d359d90..cf3d50409 100644 --- a/apps/common/froms/single_select_field.py +++ b/apps/common/forms/single_select_field.py @@ -8,7 +8,7 @@ """ from typing import List, Dict -from common.froms.base_field import TriggerType, BaseExecField +from common.forms.base_field import TriggerType, BaseExecField class SingleSelect(BaseExecField): diff --git a/apps/common/froms/tab_card.py b/apps/common/forms/tab_card.py similarity index 94% rename from apps/common/froms/tab_card.py rename to apps/common/forms/tab_card.py index f73999431..7907714bd 100644 --- a/apps/common/froms/tab_card.py +++ b/apps/common/forms/tab_card.py @@ -8,7 +8,7 @@ """ from typing import Dict -from common.froms.base_field import BaseExecField, TriggerType +from common.forms.base_field import BaseExecField, TriggerType class TabCard(BaseExecField): diff --git a/apps/common/froms/table_checkbox.py b/apps/common/forms/table_checkbox.py similarity index 94% rename from apps/common/froms/table_checkbox.py rename to apps/common/forms/table_checkbox.py index 16d61ddb2..e01f14d31 100644 --- a/apps/common/froms/table_checkbox.py +++ b/apps/common/forms/table_checkbox.py @@ -8,7 +8,7 @@ """ from typing import Dict -from common.froms.base_field import TriggerType, BaseExecField +from common.forms.base_field import TriggerType, BaseExecField class TableRadio(BaseExecField): diff --git a/apps/common/froms/table_radio.py b/apps/common/forms/table_radio.py similarity index 94% rename from apps/common/froms/table_radio.py rename to apps/common/forms/table_radio.py index 0c2ef2fc6..3b4c2bfb0 100644 --- a/apps/common/froms/table_radio.py +++ b/apps/common/forms/table_radio.py @@ -8,7 +8,7 @@ """ from typing import Dict -from common.froms.base_field import TriggerType, BaseExecField +from common.forms.base_field import TriggerType, BaseExecField class TableRadio(BaseExecField): diff --git a/apps/common/froms/text_input_field.py b/apps/common/forms/text_input_field.py similarity index 91% rename from apps/common/froms/text_input_field.py rename to apps/common/forms/text_input_field.py index eeb16782b..28a821e15 100644 --- a/apps/common/froms/text_input_field.py +++ b/apps/common/forms/text_input_field.py @@ -8,7 +8,7 @@ """ from typing import Dict -from common.froms.base_field import BaseField, TriggerType +from common.forms.base_field import BaseField, TriggerType class TextInputField(BaseField): diff --git a/apps/common/util/file_util.py b/apps/common/util/file_util.py index f46c460a7..447b007bc 100644 --- a/apps/common/util/file_util.py +++ b/apps/common/util/file_util.py @@ -9,8 +9,6 @@ def get_file_content(path): - file = open(path, "r", - encoding='utf-8') - content = file.read() - file.close() + with open(path, "r", encoding='utf-8') as file: + content = file.read() return content diff --git a/apps/common/util/split_model.py b/apps/common/util/split_model.py index 16945e0ec..19b265fc6 100644 --- a/apps/common/util/split_model.py +++ b/apps/common/util/split_model.py @@ -336,6 +336,7 @@ class SplitModel: :return: 解析后数据 {content:段落数据,keywords:[‘段落关键词’],parent_chain:['段落父级链路']} """ text = text.replace('\r', '\n') + text = text.replace("\0", '') result_tree = self.parse_to_tree(text, 0) result = result_tree_to_paragraph(result_tree, [], []) return [item for item in [self.post_reset_paragraph(row) for row in result] if diff --git a/apps/setting/models_provider/constants/model_provider_constants.py b/apps/setting/models_provider/constants/model_provider_constants.py index dad4f89fc..3816795e5 100644 --- a/apps/setting/models_provider/constants/model_provider_constants.py +++ b/apps/setting/models_provider/constants/model_provider_constants.py @@ -11,7 +11,9 @@ from enum import Enum from setting.models_provider.impl.azure_model_provider.azure_model_provider import AzureModelProvider from setting.models_provider.impl.ollama_model_provider.ollama_model_provider import OllamaModelProvider from setting.models_provider.impl.openai_model_provider.openai_model_provider import OpenAIModelProvider +from setting.models_provider.impl.qwen_model_provider.qwen_model_provider import QwenModelProvider from setting.models_provider.impl.wenxin_model_provider.wenxin_model_provider import WenxinModelProvider +from setting.models_provider.impl.kimi_model_provider.kimi_model_provider import KimiModelProvider class ModelProvideConstants(Enum): @@ -19,3 +21,5 @@ class ModelProvideConstants(Enum): model_wenxin_provider = WenxinModelProvider() model_ollama_provider = OllamaModelProvider() model_openai_provider = OpenAIModelProvider() + model_kimi_provider = KimiModelProvider() + model_qwen_provider = QwenModelProvider() diff --git a/apps/setting/models_provider/impl/azure_model_provider/azure_model_provider.py b/apps/setting/models_provider/impl/azure_model_provider/azure_model_provider.py index 35775d866..f58f8744c 100644 --- a/apps/setting/models_provider/impl/azure_model_provider/azure_model_provider.py +++ b/apps/setting/models_provider/impl/azure_model_provider/azure_model_provider.py @@ -12,9 +12,9 @@ from typing import Dict from langchain.schema import HumanMessage from langchain_community.chat_models.azure_openai import AzureChatOpenAI -from common import froms +from common import forms from common.exception.app_exception import AppApiException -from common.froms import BaseForm +from common.forms import BaseForm from common.util.file_util import get_file_content from setting.models_provider.base_model_provider import IModelProvider, ModelProvideInfo, BaseModelCredential, \ ModelInfo, \ @@ -51,11 +51,11 @@ class AzureLLMModelCredential(BaseForm, BaseModelCredential): def encryption_dict(self, model: Dict[str, object]): return {**model, 'api_key': super().encryption(model.get('api_key', ''))} - api_base = froms.TextInputField('API 域名', required=True) + api_base = forms.TextInputField('API 域名', required=True) - api_key = froms.PasswordInputField("API Key", required=True) + api_key = forms.PasswordInputField("API Key", required=True) - deployment_name = froms.TextInputField("部署名", required=True) + deployment_name = forms.TextInputField("部署名", required=True) class DefaultAzureLLMModelCredential(BaseForm, BaseModelCredential): @@ -87,13 +87,13 @@ class DefaultAzureLLMModelCredential(BaseForm, BaseModelCredential): def encryption_dict(self, model: Dict[str, object]): return {**model, 'api_key': super().encryption(model.get('api_key', ''))} - api_version = froms.TextInputField("api_version", required=True) + api_version = forms.TextInputField("api_version", required=True) - api_base = froms.TextInputField('API 域名', required=True) + api_base = forms.TextInputField('API 域名', required=True) - api_key = froms.PasswordInputField("API Key", required=True) + api_key = forms.PasswordInputField("API Key", required=True) - deployment_name = froms.TextInputField("部署名", required=True) + deployment_name = forms.TextInputField("部署名", required=True) azure_llm_model_credential = AzureLLMModelCredential() diff --git a/apps/setting/models_provider/impl/kimi_model_provider/__init__.py b/apps/setting/models_provider/impl/kimi_model_provider/__init__.py new file mode 100644 index 000000000..53b7001e5 --- /dev/null +++ b/apps/setting/models_provider/impl/kimi_model_provider/__init__.py @@ -0,0 +1,8 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: __init__.py.py + @date:2023/10/31 17:16 + @desc: +""" diff --git a/apps/setting/models_provider/impl/kimi_model_provider/icon/kimi_icon_svg b/apps/setting/models_provider/impl/kimi_model_provider/icon/kimi_icon_svg new file mode 100644 index 000000000..80bfcabff --- /dev/null +++ b/apps/setting/models_provider/impl/kimi_model_provider/icon/kimi_icon_svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/apps/setting/models_provider/impl/kimi_model_provider/kimi_model_provider.py b/apps/setting/models_provider/impl/kimi_model_provider/kimi_model_provider.py new file mode 100644 index 000000000..6394e5902 --- /dev/null +++ b/apps/setting/models_provider/impl/kimi_model_provider/kimi_model_provider.py @@ -0,0 +1,109 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: kimi_model_provider.py + @date:2024/3/28 16:26 + @desc: +""" +import os +from typing import Dict + +from langchain.schema import HumanMessage +from langchain.chat_models.base import BaseChatModel + + +from common import forms +from common.exception.app_exception import AppApiException +from common.forms import BaseForm +from common.util.file_util import get_file_content +from setting.models_provider.base_model_provider import IModelProvider, ModelProvideInfo, BaseModelCredential, \ + ModelInfo, \ + ModelTypeConst, ValidCode +from smartdoc.conf import PROJECT_DIR +from setting.models_provider.impl.kimi_model_provider.model.kimi_chat_model import KimiChatModel + + + + +class KimiLLMModelCredential(BaseForm, BaseModelCredential): + + def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], raise_exception=False): + model_type_list = KimiModelProvider().get_model_type_list() + if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))): + raise AppApiException(ValidCode.valid_error.value, f'{model_type} 模型类型不支持') + + for key in ['api_base', 'api_key']: + if key not in model_credential: + if raise_exception: + raise AppApiException(ValidCode.valid_error.value, f'{key} 字段为必填字段') + else: + return False + try: + # llm_kimi = Moonshot( + # model_name=model_name, + # base_url=model_credential['api_base'], + # moonshot_api_key=model_credential['api_key'] + # ) + + model = KimiModelProvider().get_model(model_type, model_name, model_credential) + model.invoke([HumanMessage(content='你好')]) + except Exception as e: + if isinstance(e, AppApiException): + raise e + if raise_exception: + raise AppApiException(ValidCode.valid_error.value, f'校验失败,请检查参数是否正确: {str(e)}') + else: + return False + return True + + def encryption_dict(self, model: Dict[str, object]): + return {**model, 'api_key': super().encryption(model.get('api_key', ''))} + + api_base = forms.TextInputField('API 域名', required=True) + api_key = forms.PasswordInputField('API Key', required=True) + + +kimi_llm_model_credential = KimiLLMModelCredential() + +model_dict = { + 'moonshot-v1-8k': ModelInfo('moonshot-v1-8k', '', ModelTypeConst.LLM, kimi_llm_model_credential, + ), + 'moonshot-v1-32k': ModelInfo('moonshot-v1-32k', '', ModelTypeConst.LLM, kimi_llm_model_credential, + ), + 'moonshot-v1-128k': ModelInfo('moonshot-v1-128k', '', ModelTypeConst.LLM, kimi_llm_model_credential, + ) +} + + +class KimiModelProvider(IModelProvider): + + def get_dialogue_number(self): + return 3 + + def get_model(self, model_type, model_name, model_credential: Dict[str, object], **model_kwargs) -> BaseChatModel: + kimi_chat_open_ai = KimiChatModel( + openai_api_base=model_credential['api_base'], + openai_api_key=model_credential['api_key'], + model_name=model_name, + ) + return kimi_chat_open_ai + + def get_model_credential(self, model_type, model_name): + if model_name in model_dict: + return model_dict.get(model_name).model_credential + return kimi_llm_model_credential + + def get_model_provide_info(self): + return ModelProvideInfo(provider='model_kimi_provider', name='Kimi', icon=get_file_content( + os.path.join(PROJECT_DIR, "apps", "setting", 'models_provider', 'impl', 'kimi_model_provider', 'icon', + 'kimi_icon_svg'))) + + def get_model_list(self, model_type: str): + if model_type is None: + raise AppApiException(500, '模型类型不能为空') + return [model_dict.get(key).to_dict() for key in + list(filter(lambda key: model_dict.get(key).model_type == model_type, model_dict.keys()))] + + def get_model_type_list(self): + return [{'key': "大语言模型", 'value': "LLM"}] diff --git a/apps/setting/models_provider/impl/kimi_model_provider/model/kimi_chat_model.py b/apps/setting/models_provider/impl/kimi_model_provider/model/kimi_chat_model.py new file mode 100644 index 000000000..c69cae48d --- /dev/null +++ b/apps/setting/models_provider/impl/kimi_model_provider/model/kimi_chat_model.py @@ -0,0 +1,36 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: kimi_chat_model.py + @date:2023/11/10 17:45 + @desc: +""" +from typing import List + +from langchain_community.chat_models import ChatOpenAI +from langchain_core.messages import BaseMessage, get_buffer_string + + +class TokenizerManage: + tokenizer = None + + @staticmethod + def get_tokenizer(): + from transformers import GPT2TokenizerFast + if TokenizerManage.tokenizer is None: + TokenizerManage.tokenizer = GPT2TokenizerFast.from_pretrained('gpt2', + cache_dir="/opt/maxkb/model/tokenizer", + resume_download=False, + force_download=False) + return TokenizerManage.tokenizer + + +class KimiChatModel(ChatOpenAI): + def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: + tokenizer = TokenizerManage.get_tokenizer() + return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages]) + + def get_num_tokens(self, text: str) -> int: + tokenizer = TokenizerManage.get_tokenizer() + return len(tokenizer.encode(text)) diff --git a/apps/setting/models_provider/impl/ollama_model_provider/ollama_model_provider.py b/apps/setting/models_provider/impl/ollama_model_provider/ollama_model_provider.py index 2d8097a70..2cc1601a3 100644 --- a/apps/setting/models_provider/impl/ollama_model_provider/ollama_model_provider.py +++ b/apps/setting/models_provider/impl/ollama_model_provider/ollama_model_provider.py @@ -14,9 +14,9 @@ from urllib.parse import urlparse, ParseResult import requests from langchain.chat_models.base import BaseChatModel -from common import froms +from common import forms from common.exception.app_exception import AppApiException -from common.froms import BaseForm +from common.forms import BaseForm from common.util.file_util import get_file_content from setting.models_provider.base_model_provider import IModelProvider, ModelProvideInfo, ModelInfo, ModelTypeConst, \ BaseModelCredential, DownModelChunk, DownModelChunkStatus, ValidCode @@ -51,8 +51,8 @@ class OllamaLLMModelCredential(BaseForm, BaseModelCredential): self.api_key = model_info.get('api_key') return self - api_base = froms.TextInputField('API 域名', required=True) - api_key = froms.PasswordInputField('API Key', required=True) + api_base = forms.TextInputField('API 域名', required=True) + api_key = forms.PasswordInputField('API Key', required=True) ollama_llm_model_credential = OllamaLLMModelCredential() diff --git a/apps/setting/models_provider/impl/openai_model_provider/model/openai_chat_model.py b/apps/setting/models_provider/impl/openai_model_provider/model/openai_chat_model.py new file mode 100644 index 000000000..1cdfa2aff --- /dev/null +++ b/apps/setting/models_provider/impl/openai_model_provider/model/openai_chat_model.py @@ -0,0 +1,42 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: openai_chat_model.py + @date:2024/4/18 15:28 + @desc: +""" +from typing import List + +from langchain_core.messages import BaseMessage, get_buffer_string +from langchain_openai import ChatOpenAI + + +class TokenizerManage: + tokenizer = None + + @staticmethod + def get_tokenizer(): + from transformers import GPT2TokenizerFast + if TokenizerManage.tokenizer is None: + TokenizerManage.tokenizer = GPT2TokenizerFast.from_pretrained('gpt2', + cache_dir="/opt/maxkb/model/tokenizer", + resume_download=False, + force_download=False) + return TokenizerManage.tokenizer + + +class OpenAIChatModel(ChatOpenAI): + def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: + try: + return super().get_num_tokens_from_messages(messages) + except Exception as e: + tokenizer = TokenizerManage.get_tokenizer() + return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages]) + + def get_num_tokens(self, text: str) -> int: + try: + return super().get_num_tokens(text) + except Exception as e: + tokenizer = TokenizerManage.get_tokenizer() + return len(tokenizer.encode(text)) diff --git a/apps/setting/models_provider/impl/openai_model_provider/openai_model_provider.py b/apps/setting/models_provider/impl/openai_model_provider/openai_model_provider.py index 55754619d..aab6ac08c 100644 --- a/apps/setting/models_provider/impl/openai_model_provider/openai_model_provider.py +++ b/apps/setting/models_provider/impl/openai_model_provider/openai_model_provider.py @@ -10,15 +10,15 @@ import os from typing import Dict from langchain.schema import HumanMessage -from langchain_openai import ChatOpenAI -from common import froms +from common import forms from common.exception.app_exception import AppApiException -from common.froms import BaseForm +from common.forms import BaseForm from common.util.file_util import get_file_content from setting.models_provider.base_model_provider import IModelProvider, ModelProvideInfo, BaseModelCredential, \ ModelInfo, \ ModelTypeConst, ValidCode +from setting.models_provider.impl.openai_model_provider.model.openai_chat_model import OpenAIChatModel from smartdoc.conf import PROJECT_DIR @@ -50,8 +50,8 @@ class OpenAILLMModelCredential(BaseForm, BaseModelCredential): def encryption_dict(self, model: Dict[str, object]): return {**model, 'api_key': super().encryption(model.get('api_key', ''))} - api_base = froms.TextInputField('API 域名', required=True) - api_key = froms.PasswordInputField('API Key', required=True) + api_base = forms.TextInputField('API 域名', required=True) + api_key = forms.PasswordInputField('API Key', required=True) openai_llm_model_credential = OpenAILLMModelCredential() @@ -71,8 +71,9 @@ class OpenAIModelProvider(IModelProvider): def get_dialogue_number(self): return 3 - def get_model(self, model_type, model_name, model_credential: Dict[str, object], **model_kwargs) -> ChatOpenAI: - azure_chat_open_ai = ChatOpenAI( + def get_model(self, model_type, model_name, model_credential: Dict[str, object], **model_kwargs) -> OpenAIChatModel: + azure_chat_open_ai = OpenAIChatModel( + model=model_name, openai_api_base=model_credential.get('api_base'), openai_api_key=model_credential.get('api_key') ) diff --git a/apps/setting/models_provider/impl/qwen_model_provider/__init__.py b/apps/setting/models_provider/impl/qwen_model_provider/__init__.py new file mode 100644 index 000000000..53b7001e5 --- /dev/null +++ b/apps/setting/models_provider/impl/qwen_model_provider/__init__.py @@ -0,0 +1,8 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: __init__.py.py + @date:2023/10/31 17:16 + @desc: +""" diff --git a/apps/setting/models_provider/impl/qwen_model_provider/icon/qwen_icon_svg b/apps/setting/models_provider/impl/qwen_model_provider/icon/qwen_icon_svg new file mode 100644 index 000000000..cb9a718af --- /dev/null +++ b/apps/setting/models_provider/impl/qwen_model_provider/icon/qwen_icon_svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/setting/models_provider/impl/qwen_model_provider/qwen_model_provider.py b/apps/setting/models_provider/impl/qwen_model_provider/qwen_model_provider.py new file mode 100644 index 000000000..46ad1c6ec --- /dev/null +++ b/apps/setting/models_provider/impl/qwen_model_provider/qwen_model_provider.py @@ -0,0 +1,92 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:虎 + @file: qwen_model_provider.py + @date:2023/10/31 16:19 + @desc: +""" +import os +from typing import Dict + +from langchain.schema import HumanMessage +from langchain_community.chat_models.tongyi import ChatTongyi + +from common import forms +from common.exception.app_exception import AppApiException +from common.forms import BaseForm +from common.util.file_util import get_file_content +from setting.models_provider.base_model_provider import ModelProvideInfo, ModelTypeConst, BaseModelCredential, \ + ModelInfo, IModelProvider, ValidCode +from smartdoc.conf import PROJECT_DIR + + +class OpenAILLMModelCredential(BaseForm, BaseModelCredential): + + def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], raise_exception=False): + model_type_list = QwenModelProvider().get_model_type_list() + if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))): + raise AppApiException(ValidCode.valid_error.value, f'{model_type} 模型类型不支持') + for key in ['api_key']: + if key not in model_credential: + if raise_exception: + raise AppApiException(ValidCode.valid_error.value, f'{key} 字段为必填字段') + else: + return False + try: + model = QwenModelProvider().get_model(model_type, model_name, model_credential) + model.invoke([HumanMessage(content='你好')]) + except Exception as e: + if isinstance(e, AppApiException): + raise e + if raise_exception: + raise AppApiException(ValidCode.valid_error.value, f'校验失败,请检查参数是否正确: {str(e)}') + else: + return False + return True + + def encryption_dict(self, model: Dict[str, object]): + return {**model, 'api_key': super().encryption(model.get('api_key', ''))} + + api_key = forms.PasswordInputField('API Key', required=True) + + +qwen_model_credential = OpenAILLMModelCredential() + +model_dict = { + 'qwen-turbo': ModelInfo('qwen-turbo', '', ModelTypeConst.LLM, qwen_model_credential), + 'qwen-plus': ModelInfo('qwen-plus', '', ModelTypeConst.LLM, qwen_model_credential), + 'qwen-max': ModelInfo('qwen-max', '', ModelTypeConst.LLM, qwen_model_credential) +} + + +class QwenModelProvider(IModelProvider): + + def get_dialogue_number(self): + return 3 + + def get_model(self, model_type, model_name, model_credential: Dict[str, object], **model_kwargs) -> ChatTongyi: + chat_tong_yi = ChatTongyi( + model_name=model_name, + dashscope_api_key=model_credential.get('api_key') + ) + return chat_tong_yi + + def get_model_credential(self, model_type, model_name): + if model_name in model_dict: + return model_dict.get(model_name).model_credential + return qwen_model_credential + + def get_model_provide_info(self): + return ModelProvideInfo(provider='model_qwen_provider', name='通义千问', icon=get_file_content( + os.path.join(PROJECT_DIR, "apps", "setting", 'models_provider', 'impl', 'qwen_model_provider', 'icon', + 'qwen_icon_svg'))) + + def get_model_list(self, model_type: str): + if model_type is None: + raise AppApiException(500, '模型类型不能为空') + return [model_dict.get(key).to_dict() for key in + list(filter(lambda key: model_dict.get(key).model_type == model_type, model_dict.keys()))] + + def get_model_type_list(self): + return [{'key': "大语言模型", 'value': "LLM"}] diff --git a/apps/setting/models_provider/impl/wenxin_model_provider/wenxin_model_provider.py b/apps/setting/models_provider/impl/wenxin_model_provider/wenxin_model_provider.py index 2805f4b1d..3d7c9a7d9 100644 --- a/apps/setting/models_provider/impl/wenxin_model_provider/wenxin_model_provider.py +++ b/apps/setting/models_provider/impl/wenxin_model_provider/wenxin_model_provider.py @@ -13,9 +13,9 @@ from langchain.schema import HumanMessage from langchain_community.chat_models import QianfanChatEndpoint from qianfan import ChatCompletion -from common import froms +from common import forms from common.exception.app_exception import AppApiException -from common.froms import BaseForm +from common.forms import BaseForm from common.util.file_util import get_file_content from setting.models_provider.base_model_provider import ModelProvideInfo, ModelTypeConst, BaseModelCredential, \ ModelInfo, IModelProvider, ValidCode @@ -55,9 +55,9 @@ class WenxinLLMModelCredential(BaseForm, BaseModelCredential): self.secret_key = model_info.get('secret_key') return self - api_key = froms.PasswordInputField('API Key', required=True) + api_key = forms.PasswordInputField('API Key', required=True) - secret_key = froms.PasswordInputField("Secret Key", required=True) + secret_key = forms.PasswordInputField("Secret Key", required=True) win_xin_llm_model_credential = WenxinLLMModelCredential() diff --git a/apps/smartdoc/conf.py b/apps/smartdoc/conf.py index 855b507a1..27e1e8b08 100644 --- a/apps/smartdoc/conf.py +++ b/apps/smartdoc/conf.py @@ -174,7 +174,7 @@ class ConfigManager: return True def load_from_yml(self): - for i in ['config_example.yml', 'config.yaml']: + for i in ['config_example.yml', 'config.yaml', 'config.yml']: if not os.path.isfile(os.path.join(self.root_path, i)): continue loaded = self.from_yaml(i) @@ -193,11 +193,12 @@ class ConfigManager: if manager.load_from_yml(): config = manager.config else: - msg = """ + msg = f""" Error: No config file found. - You can run `cp config_example.yml config_example.yml`, and edit it. + You can run `cp config_example.yml {root_path}/config.yml`, and edit it. + """ raise ImportError(msg) return config diff --git a/apps/smartdoc/urls.py b/apps/smartdoc/urls.py index a7f99c1ee..9e85a1874 100644 --- a/apps/smartdoc/urls.py +++ b/apps/smartdoc/urls.py @@ -5,13 +5,13 @@ The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/4.2/topics/http/urls/ Examples: Function views - 1. Add an import: froms my_app import views + 1. Add an import: forms my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views - 1. Add an import: froms other_app.views import Home + 1. Add an import: forms other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf - 1. Import the include() function: froms django.urls import include, path + 1. Import the include() function: forms django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ import os diff --git a/apps/users/views/user.py b/apps/users/views/user.py index a6fa856f1..e691ff4b9 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -88,8 +88,7 @@ class ResetCurrentUserPasswordView(APIView): data.update(request.data) serializer_obj = RePasswordSerializer(data=data) if serializer_obj.reset_password(): - token_cache.delete(request.META.get('HTTP_AUTHORIZATION', None - )) + token_cache.delete(request.META.get('HTTP_AUTHORIZATION')) return result.success(True) return result.error("修改密码失败") @@ -119,8 +118,7 @@ class Logout(APIView): responses=SendEmailSerializer().get_response_body_api(), tags=['用户']) def post(self, request: Request): - token_cache.delete(request.META.get('HTTP_AUTHORIZATION', None - )) + token_cache.delete(request.META.get('HTTP_AUTHORIZATION')) return result.success(True) diff --git a/installer/run-maxkb.sh b/installer/run-maxkb.sh index b60cd5bf7..597da7f02 100644 --- a/installer/run-maxkb.sh +++ b/installer/run-maxkb.sh @@ -1,9 +1,9 @@ #!/bin/bash -# Start postgress +# Start postgresql docker-entrypoint.sh postgres & sleep 10 -# Wait postgress +# Wait postgresql until pg_isready --host=127.0.0.1; do sleep 1 && echo "waiting for postgres"; done # Start MaxKB diff --git a/pyproject.toml b/pyproject.toml index cbe0e5dc2..d300f6195 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ django-apscheduler = "^0.6.2" pymupdf = "^1.24.0" python-docx = "^1.1.0" xlwt = "^1.3.0" +dashscope = "^1.17.0" [build-system] requires = ["poetry-core"] diff --git a/ui/src/api/application-overview.ts b/ui/src/api/application-overview.ts index d77da6322..e6a4d2c4d 100644 --- a/ui/src/api/application-overview.ts +++ b/ui/src/api/application-overview.ts @@ -7,64 +7,64 @@ const prefix = '/application' /** * API_KEY列表 - * @param 参数 applicaiton_id + * @param 参数 application_id */ -const getAPIKey: (applicaiton_id: string, loading?: Ref) => Promise> = ( - applicaiton_id, +const getAPIKey: (application_id: string, loading?: Ref) => Promise> = ( + application_id, loading ) => { - return get(`${prefix}/${applicaiton_id}/api_key`, undefined, loading) + return get(`${prefix}/${application_id}/api_key`, undefined, loading) } /** * 新增API_KEY - * @param 参数 applicaiton_id + * @param 参数 application_id */ -const postAPIKey: (applicaiton_id: string, loading?: Ref) => Promise> = ( - applicaiton_id, +const postAPIKey: (application_id: string, loading?: Ref) => Promise> = ( + application_id, loading ) => { - return post(`${prefix}/${applicaiton_id}/api_key`, {}, undefined, loading) + return post(`${prefix}/${application_id}/api_key`, {}, undefined, loading) } /** * 删除API_KEY - * @param 参数 applicaiton_id api_key_id + * @param 参数 application_id api_key_id */ const delAPIKey: ( - applicaiton_id: String, + application_id: String, api_key_id: String, loading?: Ref -) => Promise> = (applicaiton_id, api_key_id, loading) => { - return del(`${prefix}/${applicaiton_id}/api_key/${api_key_id}`, undefined, undefined, loading) +) => Promise> = (application_id, api_key_id, loading) => { + return del(`${prefix}/${application_id}/api_key/${api_key_id}`, undefined, undefined, loading) } /** * 修改API_KEY - * @param 参数 applicaiton_id,api_key_id + * @param 参数 application_id,api_key_id * data { * is_active: boolean * } */ const putAPIKey: ( - applicaiton_id: string, + application_id: string, api_key_id: String, data: any, loading?: Ref -) => Promise> = (applicaiton_id, api_key_id, data, loading) => { - return put(`${prefix}/${applicaiton_id}/api_key/${api_key_id}`, data, undefined, loading) +) => Promise> = (application_id, api_key_id, data, loading) => { + return put(`${prefix}/${application_id}/api_key/${api_key_id}`, data, undefined, loading) } /** * 统计 - * @param 参数 applicaiton_id, data + * @param 参数 application_id, data */ const getStatistics: ( - applicaiton_id: string, + application_id: string, data: any, loading?: Ref -) => Promise> = (applicaiton_id, data, loading) => { - return get(`${prefix}/${applicaiton_id}/statistics/chat_record_aggregate_trend`, data, loading) +) => Promise> = (application_id, data, loading) => { + return get(`${prefix}/${application_id}/statistics/chat_record_aggregate_trend`, data, loading) } export default { diff --git a/ui/src/api/application.ts b/ui/src/api/application.ts index 888e6f495..c83ff3c8e 100644 --- a/ui/src/api/application.ts +++ b/ui/src/api/application.ts @@ -49,70 +49,70 @@ const postApplication: ( */ const putApplication: ( - applicaiton_id: String, + application_id: String, data: ApplicationFormType, loading?: Ref -) => Promise> = (applicaiton_id, data, loading) => { - return put(`${prefix}/${applicaiton_id}`, data, undefined, loading) +) => Promise> = (application_id, data, loading) => { + return put(`${prefix}/${application_id}`, data, undefined, loading) } /** * 删除应用 - * @param 参数 applicaiton_id + * @param 参数 application_id */ const delApplication: ( - applicaiton_id: String, + application_id: String, loading?: Ref -) => Promise> = (applicaiton_id, loading) => { - return del(`${prefix}/${applicaiton_id}`, undefined, {}, loading) +) => Promise> = (application_id, loading) => { + return del(`${prefix}/${application_id}`, undefined, {}, loading) } /** * 应用详情 - * @param 参数 applicaiton_id + * @param 参数 application_id */ const getApplicationDetail: ( - applicaiton_id: string, + application_id: string, loading?: Ref -) => Promise> = (applicaiton_id, loading) => { - return get(`${prefix}/${applicaiton_id}`, undefined, loading) +) => Promise> = (application_id, loading) => { + return get(`${prefix}/${application_id}`, undefined, loading) } /** * 获得当前应用可使用的知识库 - * @param 参数 applicaiton_id + * @param 参数 application_id */ const getApplicationDataset: ( - applicaiton_id: string, + application_id: string, loading?: Ref -) => Promise> = (applicaiton_id, loading) => { - return get(`${prefix}/${applicaiton_id}/list_dataset`, undefined, loading) +) => Promise> = (application_id, loading) => { + return get(`${prefix}/${application_id}/list_dataset`, undefined, loading) } /** * 获取AccessToken - * @param 参数 applicaiton_id + * @param 参数 application_id */ -const getAccessToken: (applicaiton_id: string, loading?: Ref) => Promise> = ( - applicaiton_id, +const getAccessToken: (application_id: string, loading?: Ref) => Promise> = ( + application_id, loading ) => { - return get(`${prefix}/${applicaiton_id}/access_token`, undefined, loading) + return get(`${prefix}/${application_id}/access_token`, undefined, loading) } /** * 修改AccessToken - * @param 参数 applicaiton_id + * @param 参数 application_id * data { * "is_active": true * } */ const putAccessToken: ( - applicaiton_id: string, + application_id: string, data: any, loading?: Ref -) => Promise> = (applicaiton_id, data, loading) => { - return put(`${prefix}/${applicaiton_id}/access_token`, data, undefined, loading) +) => Promise> = (application_id, data, loading) => { + return put(`${prefix}/${application_id}/access_token`, data, undefined, loading) } /** @@ -161,8 +161,8 @@ const postChatOpen: (data: ApplicationFormType) => Promise> = (data) ] } */ -const getChatOpen: (applicaiton_id: String) => Promise> = (applicaiton_id) => { - return get(`${prefix}/${applicaiton_id}/chat/open`) +const getChatOpen: (application_id: String) => Promise> = (application_id) => { + return get(`${prefix}/${application_id}/chat/open`) } /** * 对话 diff --git a/ui/src/api/dataset.ts b/ui/src/api/dataset.ts index a95395b45..850626cf4 100644 --- a/ui/src/api/dataset.ts +++ b/ui/src/api/dataset.ts @@ -17,7 +17,7 @@ const prefix = '/dataset' "name": "string", } */ -const getDateset: ( +const getDataset: ( page: pageRequest, param: any, loading?: Ref @@ -29,7 +29,7 @@ const getDateset: ( * 获取全部知识库 * @param 参数 */ -const getAllDateset: (loading?: Ref) => Promise> = (loading) => { +const getAllDataset: (loading?: Ref) => Promise> = (loading) => { return get(`${prefix}`, undefined, loading) } @@ -37,7 +37,7 @@ const getAllDateset: (loading?: Ref) => Promise> = (loadi * 删除知识库 * @param 参数 dataset_id */ -const delDateset: (dataset_id: String, loading?: Ref) => Promise> = ( +const delDataset: (dataset_id: String, loading?: Ref) => Promise> = ( dataset_id, loading ) => { @@ -69,7 +69,7 @@ const delDateset: (dataset_id: String, loading?: Ref) => Promise) => Promise> = ( +const postDataset: (data: datasetData, loading?: Ref) => Promise> = ( data, loading ) => { @@ -86,7 +86,7 @@ const postDateset: (data: datasetData, loading?: Ref) => Promise) => Promise> = ( +const postWebDataset: (data: any, loading?: Ref) => Promise> = ( data, loading ) => { @@ -97,7 +97,7 @@ const postWebDateset: (data: any, loading?: Ref) => Promise * 知识库详情 * @param 参数 dataset_id */ -const getDatesetDetail: (dataset_id: string, loading?: Ref) => Promise> = ( +const getDatasetDetail: (dataset_id: string, loading?: Ref) => Promise> = ( dataset_id, loading ) => { @@ -113,7 +113,7 @@ const getDatesetDetail: (dataset_id: string, loading?: Ref) => Promise< "desc": true } */ -const putDateset: (dataset_id: string, data: any) => Promise> = ( +const putDataset: (dataset_id: string, data: any) => Promise> = ( dataset_id, data: any ) => { @@ -152,7 +152,7 @@ const getDatasetHitTest: ( * @param 参数 dataset_id * @query 参数 sync_type // 同步类型->replace:替换同步,complete:完整同步 */ -const putSyncWebDateset: ( +const putSyncWebDataset: ( dataset_id: string, sync_type: string, loading?: Ref @@ -161,14 +161,14 @@ const putSyncWebDateset: ( } export default { - getDateset, - getAllDateset, - delDateset, - postDateset, - getDatesetDetail, - putDateset, + getDataset, + getAllDataset, + delDataset, + postDataset, + getDatasetDetail, + putDataset, listUsableApplication, getDatasetHitTest, - postWebDateset, - putSyncWebDateset + postWebDataset, + putSyncWebDataset } diff --git a/ui/src/api/type/application.ts b/ui/src/api/type/application.ts index 8cd8d5437..eefd8f31f 100644 --- a/ui/src/api/type/application.ts +++ b/ui/src/api/type/application.ts @@ -51,19 +51,23 @@ export class ChatRecordManage { this.loading.value = true } this.id = setInterval(() => { - const s = this.chat.buffer.shift() - if (s !== undefined) { - this.chat.answer_text = this.chat.answer_text + s + if (this.chat.buffer.length > 20) { + this.chat.answer_text = + this.chat.answer_text + this.chat.buffer.splice(0, this.chat.buffer.length - 20).join('') + } else if (this.is_close) { + this.chat.answer_text = this.chat.answer_text + this.chat.buffer.join('') + this.chat.write_ed = true + this.write_ed = true + if (this.loading) { + this.loading.value = false + } + if (this.id) { + clearInterval(this.id) + } } else { - if (this.is_close) { - this.chat.write_ed = true - this.write_ed = true - if (this.loading) { - this.loading.value = false - } - if (this.id) { - clearInterval(this.id) - } + const s = this.chat.buffer.shift() + if (s !== undefined) { + this.chat.answer_text = this.chat.answer_text + s } } }, this.ms) diff --git a/ui/src/assets/hit-test-empty.png b/ui/src/assets/hit-test-empty.png new file mode 100644 index 000000000..83a2c9a06 Binary files /dev/null and b/ui/src/assets/hit-test-empty.png differ diff --git a/ui/src/components/ai-chat/index.vue b/ui/src/components/ai-chat/index.vue index 2d3d4d753..a76a3ab12 100644 --- a/ui/src/components/ai-chat/index.vue +++ b/ui/src/components/ai-chat/index.vue @@ -14,7 +14,7 @@