diff --git a/apps/models_provider/constants/model_provider_constants.py b/apps/models_provider/constants/model_provider_constants.py index eb8494e33be..1117204a1a0 100644 --- a/apps/models_provider/constants/model_provider_constants.py +++ b/apps/models_provider/constants/model_provider_constants.py @@ -22,7 +22,7 @@ from models_provider.impl.volcanic_engine_model_provider.volcanic_engine_model_provider import ( VolcanicEngineModelProvider, ) -from models_provider.impl.wenxin_model_provider.wenxin_model_provider import WenxinModelProvider +from models_provider.impl.qianfan_model_provider.qianfan_model_provider import QianfanModelProvider from models_provider.impl.xf_model_provider.xf_model_provider import XunFeiModelProvider from models_provider.impl.xinference_model_provider.xinference_model_provider import XinferenceModelProvider from models_provider.impl.zhipu_model_provider.zhipu_model_provider import ZhiPuModelProvider @@ -30,7 +30,7 @@ class ModelProvideConstants(Enum): model_azure_provider = AzureModelProvider() - model_wenxin_provider = WenxinModelProvider() + model_qianfan_provider = QianfanModelProvider() model_ollama_provider = OllamaModelProvider() model_openai_provider = OpenAIModelProvider() model_docker_ai_provider = DockerModelProvider() diff --git a/apps/models_provider/impl/qwen_model_provider/__init__.py b/apps/models_provider/impl/qwen_model_provider/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/models_provider/impl/qwen_model_provider/credential/__init__.py b/apps/models_provider/impl/qwen_model_provider/credential/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/models_provider/impl/qwen_model_provider/model/__init__.py b/apps/models_provider/impl/qwen_model_provider/model/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/models_provider/impl/wenxin_model_provider/__init__.py b/apps/models_provider/impl/wenxin_model_provider/__init__.py deleted file mode 100644 index fd54226fe4c..00000000000 --- a/apps/models_provider/impl/wenxin_model_provider/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# coding=utf-8 -""" -@project: maxkb -@Author:虎 -@file: __init__.py.py -@date:2023/10/31 17:16 -@desc: -""" diff --git a/apps/models_provider/impl/wenxin_model_provider/credential/__init__.py b/apps/models_provider/impl/wenxin_model_provider/credential/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/models_provider/impl/wenxin_model_provider/credential/embedding.py b/apps/models_provider/impl/wenxin_model_provider/credential/embedding.py deleted file mode 100644 index edb6306fe4e..00000000000 --- a/apps/models_provider/impl/wenxin_model_provider/credential/embedding.py +++ /dev/null @@ -1,97 +0,0 @@ -# coding=utf-8 -""" -@project: MaxKB -@Author:虎 -@file: embedding.py -@date:2024/10/17 15:40 -@desc: -""" - -from typing import Dict - -from django.utils.translation import gettext as _ - -from common import forms -from common.exception.app_exception import AppApiException -from common.forms import BaseForm -from models_provider.base_model_provider import BaseModelCredential, ValidCode -from common.utils.logger import maxkb_logger - - -class QianfanEmbeddingCredential(BaseForm, BaseModelCredential): - def is_valid( - self, - model_type: str, - model_name, - model_credential: Dict[str, object], - model_params, - provider, - raise_exception=False, - ): - api_version = model_credential.get("api_version", "v1") - model = provider.get_model(model_type, model_name, model_credential, **model_params) - if api_version == "v1": - model_type_list = provider.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, - _("{model_type} Model type is not supported").format(model_type=model_type), - ) - model_info = [model.lower() for model in model.client.models()] - if not model_info.__contains__(model_name.lower()): - raise AppApiException( - ValidCode.valid_error.value, - _("{model_name} The model does not support").format(model_name=model_name), - ) - required_keys = ["qianfan_ak", "qianfan_sk"] - if api_version == "v2": - required_keys = ["api_base", "qianfan_ak"] - - for key in required_keys: - if key not in model_credential: - if raise_exception: - raise AppApiException(ValidCode.valid_error.value, _("{key} is required").format(key=key)) - else: - return False - try: - model = provider.get_model(model_type, model_name, model_credential) - model.embed_query(_("Hello")) - except Exception as e: - maxkb_logger.error(f"Exception: {e}", exc_info=True) - if isinstance(e, AppApiException): - raise e - if raise_exception: - raise AppApiException( - ValidCode.valid_error.value, - _("Verification failed, please check whether the parameters are correct: {error}").format( - error=str(e) - ), - ) - else: - return False - return True - - def encryption_dict(self, model: Dict[str, object]): - api_version = model.get("api_version", "v1") - if api_version == "v1": - return {**model, "qianfan_sk": super().encryption(model.get("qianfan_sk", ""))} - else: # v2 - return {**model, "qianfan_ak": super().encryption(model.get("qianfan_ak", ""))} - - api_version = forms.Radio( - "API Version", - required=True, - text_field="label", - value_field="value", - option_list=[{"label": "v1", "value": "v1"}, {"label": "v2", "value": "v2"}], - default_value="v1", - provider="", - method="", - ) - - # v2版本字段 - api_base = forms.TextInputField("API URL", required=True, relation_show_field_dict={"api_version": ["v2"]}) - - # v1版本字段 - qianfan_ak = forms.PasswordInputField("API Key", required=True) - qianfan_sk = forms.PasswordInputField("Secret Key", required=True, relation_show_field_dict={"api_version": ["v1"]}) diff --git a/apps/models_provider/impl/wenxin_model_provider/credential/llm.py b/apps/models_provider/impl/wenxin_model_provider/credential/llm.py deleted file mode 100644 index d63a94efe72..00000000000 --- a/apps/models_provider/impl/wenxin_model_provider/credential/llm.py +++ /dev/null @@ -1,142 +0,0 @@ -# coding=utf-8 -""" -@project: MaxKB -@Author:虎 -@file: llm.py -@date:2024/7/12 10:19 -@desc: -""" - -from typing import Dict - -from django.utils.translation import gettext_lazy as _, gettext -from langchain_core.messages import HumanMessage - -from common import forms -from common.exception.app_exception import AppApiException -from common.forms import BaseForm, TooltipLabel -from models_provider.base_model_provider import BaseModelCredential, ValidCode -from common.utils.logger import maxkb_logger - - -class WenxinLLMModelParams(BaseForm): - temperature = forms.SliderField( - TooltipLabel( - _("Temperature"), - _("Higher values make the output more random, while lower values make it more focused and deterministic"), - ), - required=True, - default_value=0.95, - _min=0.1, - _max=1.0, - _step=0.01, - precision=2, - ) - - max_tokens = forms.SliderField( - TooltipLabel( - _("Output the maximum Tokens"), _("Specify the maximum number of tokens that the model can generate") - ), - required=True, - default_value=1024, - _min=2, - _max=100000, - _step=1, - precision=0, - ) - - -class WenxinLLMModelCredential(BaseForm, BaseModelCredential): - def is_valid( - self, - model_type: str, - model_name, - model_credential: Dict[str, object], - model_params, - provider, - raise_exception=False, - ): - # 根据api_version检查必需字段 - api_version = model_credential.get("api_version", "v1") - model = provider.get_model(model_type, model_name, model_credential, **{**model_params, "max_tokens": 1}) - if api_version == "v1": - model_type_list = provider.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, - gettext("{model_type} Model type is not supported").format(model_type=model_type), - ) - model_info = [model.lower() for model in model.client.models()] - if not model_info.__contains__(model_name.lower()): - raise AppApiException( - ValidCode.valid_error.value, - gettext("{model_name} The model does not support").format(model_name=model_name), - ) - required_keys = ["api_key", "secret_key"] - if api_version == "v2": - required_keys = ["api_base", "api_key"] - - for key in required_keys: - if key not in model_credential: - if raise_exception: - raise AppApiException(ValidCode.valid_error.value, gettext("{key} is required").format(key=key)) - else: - return False - try: - model.invoke([HumanMessage(content="1")]) - except Exception as e: - maxkb_logger.error(f"Exception: {e}", exc_info=True) - raise e - return True - - def encryption_dict(self, model_info: Dict[str, object]): - # 根据api_version加密不同字段 - api_version = model_info.get("api_version", "v1") - if api_version == "v1": - return {**model_info, "secret_key": super().encryption(model_info.get("secret_key", ""))} - else: # v2 - return {**model_info, "api_key": super().encryption(model_info.get("api_key", ""))} - - def build_model(self, model_info: Dict[str, object]): - api_version = model_info.get("api_version", "v1") - # 根据api_version检查必需字段 - if api_version == "v1": - for key in ["api_version", "api_key", "secret_key", "model"]: - if key not in model_info: - raise AppApiException(500, gettext("{key} is required").format(key=key)) - self.api_key = model_info.get("api_key") - self.secret_key = model_info.get("secret_key") - else: # v2 - for key in [ - "api_version", - "api_base", - "api_key", - "model", - ]: - if key not in model_info: - raise AppApiException(500, gettext("{key} is required").format(key=key)) - self.api_base = model_info.get("api_base") - self.api_key = model_info.get("api_key") - return self - - # 动态字段定义 - 根据api_version显示不同字段 - api_version = forms.Radio( - "API Version", - required=True, - text_field="label", - value_field="value", - option_list=[{"label": "v1", "value": "v1"}, {"label": "v2", "value": "v2"}], - default_value="v1", - provider="", - method="", - ) - - # v2版本字段 - api_base = forms.TextInputField("API URL", required=True, relation_show_field_dict={"api_version": ["v2"]}) - - # v1版本字段 - api_key = forms.PasswordInputField("API Key", required=True) - secret_key = forms.PasswordInputField("Secret Key", required=True, relation_show_field_dict={"api_version": ["v1"]}) - - def get_model_params_setting_form(self, model_name): - return WenxinLLMModelParams() diff --git a/apps/models_provider/impl/wenxin_model_provider/credential/reranker.py b/apps/models_provider/impl/wenxin_model_provider/credential/reranker.py deleted file mode 100644 index f63a84756df..00000000000 --- a/apps/models_provider/impl/wenxin_model_provider/credential/reranker.py +++ /dev/null @@ -1,74 +0,0 @@ -from typing import Dict - -from langchain_core.documents import Document - -from common import forms -from common.exception.app_exception import AppApiException -from common.forms import BaseForm, TooltipLabel -from models_provider.base_model_provider import BaseModelCredential, ValidCode -from django.utils.translation import gettext_lazy as _ -from common.utils.logger import maxkb_logger -from models_provider.impl.wenxin_model_provider.model.reranker import QfBgeReranker - - -class QfRerankerModelParams(BaseForm): - top_n = forms.SliderField( - TooltipLabel(_("Top N"), _("Number of top documents to return after reranking")), - required=True, - default_value=3, - _min=1, - _max=100, - _step=1, - precision=0, - ) - - -class QfRerankerCredential(BaseForm, BaseModelCredential): - api_url = forms.TextInputField("API URL", required=True) - api_key = forms.PasswordInputField("API Key", required=True) - - def is_valid( - self, - model_type: str, - model_name, - model_credential: Dict[str, object], - model_params, - provider, - raise_exception=True, - ): - model_type_list = provider.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, _("{model_type} Model type is not supported").format(model_type=model_type) - ) - - for key in ["api_url", "api_key"]: - if key not in model_credential: - if raise_exception: - raise AppApiException(ValidCode.valid_error.value, _("{key} is required").format(key=key)) - else: - return False - try: - model: QfBgeReranker = provider.get_model(model_type, model_name, model_credential) - test_text = str(_("Hello")) - model.compress_documents([Document(page_content=test_text)], test_text) - except Exception as e: - maxkb_logger.error(f"Exception: {e}", exc_info=True) - if isinstance(e, AppApiException): - raise e - if raise_exception: - raise AppApiException( - ValidCode.valid_error.value, - _("Verification failed, please check whether the parameters are correct: {error}").format( - error=str(e) - ), - ) - return False - - return True - - def encryption_dict(self, model_info: Dict[str, object]): - return {**model_info, "api_key": super().encryption(model_info.get("api_key", ""))} - - def get_model_params_setting_form(self, model_name: str) -> QfRerankerModelParams: - return QfRerankerModelParams() diff --git a/apps/models_provider/impl/wenxin_model_provider/icon/__init__.py b/apps/models_provider/impl/wenxin_model_provider/icon/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/models_provider/impl/wenxin_model_provider/icon/azure_icon_svg b/apps/models_provider/impl/wenxin_model_provider/icon/azure_icon_svg deleted file mode 100644 index 4added84b70..00000000000 --- a/apps/models_provider/impl/wenxin_model_provider/icon/azure_icon_svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/apps/models_provider/impl/wenxin_model_provider/model/__init__.py b/apps/models_provider/impl/wenxin_model_provider/model/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/models_provider/impl/wenxin_model_provider/model/embedding.py b/apps/models_provider/impl/wenxin_model_provider/model/embedding.py deleted file mode 100644 index b26ec2f8968..00000000000 --- a/apps/models_provider/impl/wenxin_model_provider/model/embedding.py +++ /dev/null @@ -1,77 +0,0 @@ -# coding=utf-8 -""" -@project: MaxKB -@Author:虎 -@file: embedding.py -@date:2024/10/17 16:48 -@desc: -""" - -from typing import Dict, List - -import openai - -from models_provider.base_model_provider import MaxKBBaseEmbeddingModel -from models_provider.langchain_compat.baidu_qianfan_endpoint import QianfanEmbeddingsEndpoint - - -class QianfanV1Embeddings(MaxKBBaseEmbeddingModel, QianfanEmbeddingsEndpoint): - def supports_image_embedding(self) -> bool: - return False - - @staticmethod - def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): - return QianfanV1Embeddings( - model=model_name, - qianfan_ak=model_credential.get("qianfan_ak"), - qianfan_sk=model_credential.get("qianfan_sk"), - ) - - -class QianfanV2EmbeddingModel(MaxKBBaseEmbeddingModel): - def supports_image_embedding(self) -> bool: - return False - - model_name: str - - @staticmethod - def is_cache_model(): - return False - - def __init__(self, api_key, base_url, model_name: str): - self.client = openai.OpenAI(api_key=api_key, base_url=base_url).embeddings - self.model_name = model_name - - @staticmethod - def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): - return QianfanV2EmbeddingModel( - api_key=model_credential.get("qianfan_ak"), - model_name=model_name, - base_url=model_credential.get("api_base"), - ) - - def embed_query(self, text: str): - res = self.embed_documents([text]) - return res[0] - - def embed_documents( - self, - texts: List[str], - ) -> List[List[float]]: - res = self.client.create(input=texts, model=self.model_name, encoding_format="float") - return [e.embedding for e in res.data] - - -class QianfanEmbeddings(MaxKBBaseEmbeddingModel): - def supports_image_embedding(self) -> bool: - return False - - @staticmethod - def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): - api_version = model_credential.get("api_version", "v1") - - if api_version == "v1": - return QianfanV1Embeddings.new_instance(model_type, model_name, model_credential, **model_kwargs) - if api_version == "v2": - return QianfanV2EmbeddingModel.new_instance(model_type, model_name, model_credential, **model_kwargs) - raise ValueError(f"Unsupported qianfan api_version: {api_version}") diff --git a/apps/models_provider/impl/wenxin_model_provider/model/llm.py b/apps/models_provider/impl/wenxin_model_provider/model/llm.py deleted file mode 100644 index 6d93deadbdf..00000000000 --- a/apps/models_provider/impl/wenxin_model_provider/model/llm.py +++ /dev/null @@ -1,110 +0,0 @@ -# coding=utf-8 -""" -@project: maxkb -@Author:虎 -@file: llm.py -@date:2023/11/10 17:45 -@desc: -""" - -from typing import List, Dict, Optional, Any, Iterator - -from models_provider.langchain_compat.baidu_qianfan_endpoint import ( - QianfanChatEndpoint, - _convert_dict_to_message, -) -from langchain_core.callbacks import CallbackManagerForLLMRun -from langchain_core.messages import ( - AIMessageChunk, - BaseMessage, -) -from langchain_core.outputs import ChatGenerationChunk - -from models_provider.base_model_provider import MaxKBBaseModel -from models_provider.impl.base_chat_open_ai import BaseChatOpenAI - - -class QianfanChatModelQianfan(MaxKBBaseModel, QianfanChatEndpoint): - @staticmethod - def is_cache_model(): - return False - - @staticmethod - def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): - optional_params = MaxKBBaseModel.filter_optional_params(model_kwargs) - return QianfanChatModelQianfan( - model=model_name, - qianfan_ak=model_credential.get("api_key"), - qianfan_sk=model_credential.get("secret_key"), - streaming=model_kwargs.get("streaming", False), - init_kwargs=optional_params, - ) - - usage_metadata: dict = {} - - def get_last_generation_info(self) -> Optional[Dict[str, Any]]: - return self.usage_metadata - - def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: - return self.usage_metadata.get("prompt_tokens", 0) - - def get_num_tokens(self, text: str) -> int: - return self.usage_metadata.get("completion_tokens", 0) - - def _stream( - self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any, - ) -> Iterator[ChatGenerationChunk]: - kwargs = {**self.init_kwargs, **kwargs} - params = self._convert_prompt_msg_params(messages, **kwargs) - params["stop"] = stop - params["stream"] = True - for res in self.client.do(**params): - if res: - msg = _convert_dict_to_message(res) - additional_kwargs = msg.additional_kwargs.get("function_call", {}) - if msg.content == "" or res.get("body").get("is_end"): - token_usage = res.get("body").get("usage") - self.usage_metadata = token_usage - chunk = ChatGenerationChunk( - text=res["result"], - message=AIMessageChunk( # type: ignore[call-arg] - content=msg.content, - role="assistant", - additional_kwargs=additional_kwargs, - ), - generation_info=msg.additional_kwargs, - ) - if run_manager: - run_manager.on_llm_new_token(chunk.text, chunk=chunk) - yield chunk - - -class QianfanChatModelOpenai(MaxKBBaseModel, BaseChatOpenAI): - @staticmethod - def is_cache_model(): - return False - - @staticmethod - def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): - optional_params = MaxKBBaseModel.filter_optional_params(model_kwargs) - return QianfanChatModelOpenai( - model=model_name, - openai_api_base=model_credential.get("api_base"), - openai_api_key=model_credential.get("api_key"), - extra_body=optional_params, - ) - - -class QianfanChatModel(MaxKBBaseModel): - @staticmethod - def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): - api_version = model_credential.get("api_version", "v1") - - if api_version == "v1": - return QianfanChatModelQianfan.new_instance(model_type, model_name, model_credential, **model_kwargs) - elif api_version == "v2": - return QianfanChatModelOpenai.new_instance(model_type, model_name, model_credential, **model_kwargs) diff --git a/apps/models_provider/impl/wenxin_model_provider/model/reranker.py b/apps/models_provider/impl/wenxin_model_provider/model/reranker.py deleted file mode 100644 index 430d1f0f247..00000000000 --- a/apps/models_provider/impl/wenxin_model_provider/model/reranker.py +++ /dev/null @@ -1,60 +0,0 @@ -from typing import Sequence, Optional, Dict - -import requests -from langchain_core.callbacks import Callbacks -from langchain_core.documents import BaseDocumentCompressor, Document - -from models_provider.base_model_provider import MaxKBBaseModel - - -class QfBgeReranker(MaxKBBaseModel, BaseDocumentCompressor): - api_key: str - api_url: str - model: str - params: dict - top_n: int = 3 - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.api_key = kwargs.get("api_key") - self.model = kwargs.get("model") - self.params = kwargs.get("params", {}) - self.api_url = kwargs.get("api_url") - self.top_n = self.params.get("top_n", 3) - - @staticmethod - def is_cache_model(): - return False - - @staticmethod - def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): - return QfBgeReranker( - model=model_name, - api_key=model_credential.get("api_key"), - api_url=model_credential.get("api_url"), - params=model_kwargs, - ) - - def compress_documents( - self, documents: Sequence[Document], query: str, callbacks: Optional[Callbacks] = None - ) -> Sequence[Document]: - if not documents: - return [] - - texts = [doc.page_content for doc in documents] - - headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"} - top_n = min(self.top_n, len(texts)) - payload = {"model": self.model, "query": query, "documents": texts, "top_n": top_n} - - response = requests.post(f"{self.api_url}/rerank", json=payload, headers=headers) - - if response.status_code != 200: - raise RuntimeError(f"千帆 API 请求失败:{response.text}") - - res = response.json() - - return [ - Document(page_content=item.get("document", ""), metadata={"relevance_score": item.get("relevance_score")}) - for item in res.get("results", []) - ] diff --git a/apps/models_provider/impl/wenxin_model_provider/wenxin_model_provider.py b/apps/models_provider/impl/wenxin_model_provider/wenxin_model_provider.py deleted file mode 100644 index 2f8159499b7..00000000000 --- a/apps/models_provider/impl/wenxin_model_provider/wenxin_model_provider.py +++ /dev/null @@ -1,117 +0,0 @@ -# coding=utf-8 -""" -@project: maxkb -@Author:虎 -@file: wenxin_model_provider.py -@date:2023/10/31 16:19 -@desc: -""" - -import os - -from common.utils.common import get_file_content -from models_provider.base_model_provider import ( - ModelProvideInfo, - ModelTypeConst, - ModelInfo, - IModelProvider, - ModelInfoManage, -) -from models_provider.impl.wenxin_model_provider.credential.embedding import QianfanEmbeddingCredential -from models_provider.impl.wenxin_model_provider.credential.llm import WenxinLLMModelCredential -from models_provider.impl.wenxin_model_provider.credential.reranker import QfRerankerCredential -from models_provider.impl.wenxin_model_provider.model.embedding import QianfanEmbeddings -from models_provider.impl.wenxin_model_provider.model.llm import QianfanChatModel -from maxkb.conf import PROJECT_DIR -from django.utils.translation import gettext as _ - -from models_provider.impl.wenxin_model_provider.model.reranker import QfBgeReranker - -win_xin_llm_model_credential = WenxinLLMModelCredential() -qianfan_embedding_credential = QianfanEmbeddingCredential() -qf_reranker_credential = QfRerankerCredential() -model_info_list = [ - ModelInfo( - "ERNIE-Bot-4", - _( - "ERNIE-Bot-4 is a large language model independently developed by Baidu. It covers massive Chinese data and has stronger capabilities in dialogue Q&A, content creation and generation." - ), - ModelTypeConst.LLM, - win_xin_llm_model_credential, - QianfanChatModel, - ), - ModelInfo( - "ERNIE-Bot", - _( - "ERNIE-Bot is a large language model independently developed by Baidu. It covers massive Chinese data and has stronger capabilities in dialogue Q&A, content creation and generation." - ), - ModelTypeConst.LLM, - win_xin_llm_model_credential, - QianfanChatModel, - ), - ModelInfo( - "ERNIE-Bot-turbo", - _( - "ERNIE-Bot-turbo is a large language model independently developed by Baidu. It covers massive Chinese data, has stronger capabilities in dialogue Q&A, content creation and generation, and has a faster response speed." - ), - ModelTypeConst.LLM, - win_xin_llm_model_credential, - QianfanChatModel, - ), - ModelInfo("qianfan-chinese-llama-2-13b", "", ModelTypeConst.LLM, win_xin_llm_model_credential, QianfanChatModel), - ModelInfo("ernie-4.5-turbo-32k", "", ModelTypeConst.LLM, win_xin_llm_model_credential, QianfanChatModel), - ModelInfo("ernie-speed-8k", "", ModelTypeConst.LLM, win_xin_llm_model_credential, QianfanChatModel), - ModelInfo("ernie-4.5-0.3b", "", ModelTypeConst.LLM, win_xin_llm_model_credential, QianfanChatModel), -] -embedding_model_info_list = [ - ModelInfo( - "Embedding-V1", - _( - "Embedding-V1 is a text representation model based on Baidu Wenxin large model technology. It can convert text into a vector form represented by numerical values and can be used in text retrieval, information recommendation, knowledge mining and other scenarios. Embedding-V1 provides the Embeddings interface, which can generate corresponding vector representations based on input content. You can call this interface to input text into the model and obtain the corresponding vector representation for subsequent text processing and analysis." - ), - ModelTypeConst.EMBEDDING, - qianfan_embedding_credential, - QianfanEmbeddings, - ), - ModelInfo("tao-8k", "", ModelTypeConst.EMBEDDING, qianfan_embedding_credential, QianfanEmbeddings), - ModelInfo("bge-large-zh", "", ModelTypeConst.EMBEDDING, qianfan_embedding_credential, QianfanEmbeddings), -] -rerank_model_info_list = [ - ModelInfo("bce-reranker-base", _(""), ModelTypeConst.RERANKER, qf_reranker_credential, QfBgeReranker), -] -model_info_manage = ( - ModelInfoManage.builder() - .append_model_info_list(model_info_list) - .append_default_model_info( - ModelInfo( - "ERNIE-Bot-4", - _( - "ERNIE-Bot-4 is a large language model independently developed by Baidu. It covers massive Chinese data and has stronger capabilities in dialogue Q&A, content creation and generation." - ), - ModelTypeConst.LLM, - win_xin_llm_model_credential, - QianfanChatModel, - ) - ) - .append_model_info_list(embedding_model_info_list) - .append_default_model_info(embedding_model_info_list[0]) - .append_model_info_list(rerank_model_info_list) - .append_default_model_info(rerank_model_info_list[0]) - .build() -) - - -class WenxinModelProvider(IModelProvider): - def get_model_info_manage(self): - return model_info_manage - - def get_model_provide_info(self): - return ModelProvideInfo( - provider="model_wenxin_provider", - name=_("Thousand sails large model"), - icon=get_file_content( - os.path.join( - PROJECT_DIR, "apps", "models_provider", "impl", "wenxin_model_provider", "icon", "azure_icon_svg" - ) - ), - ) diff --git a/apps/models_provider/langchain_compat/__init__.py b/apps/models_provider/langchain_compat/__init__.py index bbdc8f104a6..a2b80dea0f2 100644 --- a/apps/models_provider/langchain_compat/__init__.py +++ b/apps/models_provider/langchain_compat/__init__.py @@ -1,8 +1,3 @@ -from .baidu_qianfan_endpoint import ( - QianfanChatEndpoint, - QianfanEmbeddingsEndpoint, - _convert_dict_to_message, -) from .sparkllm import ( ChatSparkLLM, SparkLLMTextEmbeddings, @@ -12,10 +7,7 @@ __all__ = [ "ChatSparkLLM", - "QianfanChatEndpoint", - "QianfanEmbeddingsEndpoint", "SparkLLMTextEmbeddings", "_convert_delta_to_message_chunk", - "_convert_dict_to_message", "convert_message_to_dict", ] diff --git a/apps/models_provider/langchain_compat/baidu_qianfan_endpoint.py b/apps/models_provider/langchain_compat/baidu_qianfan_endpoint.py deleted file mode 100644 index 90dc4592e4b..00000000000 --- a/apps/models_provider/langchain_compat/baidu_qianfan_endpoint.py +++ /dev/null @@ -1,283 +0,0 @@ -import json -import uuid -from typing import Any, Dict, Iterator, List, Mapping, Optional, Union - -from langchain_core.callbacks import CallbackManagerForLLMRun -from langchain_core.embeddings import Embeddings -from langchain_core.language_models.chat_models import BaseChatModel -from langchain_core.messages import ( - AIMessage, - AIMessageChunk, - BaseMessage, - ChatMessage, - FunctionMessage, - HumanMessage, - SystemMessage, - ToolMessage, -) -from langchain_core.messages.ai import UsageMetadata -from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult -from langchain_core.utils import convert_to_secret_str, get_from_dict_or_env -from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator - - -def convert_message_to_dict(message: BaseMessage) -> dict: - message_dict: Dict[str, Any] - if isinstance(message, ChatMessage): - message_dict = {"role": message.role, "content": message.content} - elif isinstance(message, HumanMessage): - message_dict = {"role": "user", "content": message.content} - elif isinstance(message, AIMessage): - message_dict = {"role": "assistant", "content": message.content} - if message.tool_calls: - tool_call = message.tool_calls[0] - message_dict["function_call"] = { - "name": tool_call["name"], - "arguments": json.dumps(tool_call["args"], ensure_ascii=False), - } - message_dict["content"] = None - elif isinstance(message, (FunctionMessage, ToolMessage)): - message_dict = { - "role": "function", - "content": _create_tool_content(message.content), - "name": message.name or message.additional_kwargs.get("name"), - } - else: - raise TypeError(f"Got unknown type {message}") - return message_dict - - -def _create_tool_content(content: Union[str, List[Union[str, Dict[Any, Any]]]]) -> str: - if isinstance(content, str): - try: - if isinstance(json.loads(content), dict): - return content - except json.JSONDecodeError: - pass - return json.dumps({"tool_result": content}, ensure_ascii=False) - - -def _convert_dict_to_message(_dict: Mapping[str, Any]) -> AIMessage: - content = _dict.get("result", "") or "" - additional_kwargs: Mapping[str, Any] = {} - if _dict.get("function_call"): - additional_kwargs = {"function_call": dict(_dict["function_call"])} - if "thoughts" in additional_kwargs["function_call"]: - additional_kwargs["function_call"].pop("thoughts") - - additional_kwargs = {**_dict.get("body", {}), **additional_kwargs} - msg_additional_kwargs = { - "finish_reason": additional_kwargs.get("finish_reason", ""), - "request_id": additional_kwargs.get("id", ""), - "object": additional_kwargs.get("object", ""), - "search_info": additional_kwargs.get("search_info", []), - } - - if additional_kwargs.get("function_call", {}): - function_call = additional_kwargs.get("function_call", {}) - msg_additional_kwargs["function_call"] = function_call - msg_additional_kwargs["tool_calls"] = [ - { - "type": "function", - "function": function_call, - "id": str(uuid.uuid4()), - } - ] - - message = AIMessage(content=content, additional_kwargs=msg_additional_kwargs) - if usage := additional_kwargs.get("usage"): - message.usage_metadata = UsageMetadata( - input_tokens=usage.get("prompt_tokens", 0), - output_tokens=usage.get("completion_tokens", 0), - total_tokens=usage.get("total_tokens", 0), - ) - return message - - -class QianfanChatEndpoint(BaseChatModel): - init_kwargs: Dict[str, Any] = Field(default_factory=dict) - model_kwargs: Dict[str, Any] = Field(default_factory=dict) - client: Any = None - qianfan_ak: Optional[SecretStr] = Field(default=None, alias="api_key") - qianfan_sk: Optional[SecretStr] = Field(default=None, alias="secret_key") - streaming: bool = False - request_timeout: int = Field(60, alias="timeout") - top_p: float = 0.8 - temperature: float = 0.95 - penalty_score: float = 1 - model: Optional[str] = Field(default=None) - endpoint: Optional[str] = None - - model_config = ConfigDict(populate_by_name=True) - - @model_validator(mode="before") - @classmethod - def validate_environment(cls, values: Dict[str, Any]) -> Dict[str, Any]: - values["qianfan_ak"] = convert_to_secret_str( - get_from_dict_or_env(values, ["qianfan_ak", "api_key"], "QIANFAN_AK", default="") - ) - values["qianfan_sk"] = convert_to_secret_str( - get_from_dict_or_env(values, ["qianfan_sk", "secret_key"], "QIANFAN_SK", default="") - ) - - default_values = { - "model": values.get("model"), - "streaming": values.get("streaming", False), - **values.get("init_kwargs", {}), - } - if values["qianfan_ak"].get_secret_value(): - default_values["ak"] = values["qianfan_ak"].get_secret_value() - if values["qianfan_sk"].get_secret_value(): - default_values["sk"] = values["qianfan_sk"].get_secret_value() - if values.get("endpoint"): - default_values["endpoint"] = values["endpoint"] - - import qianfan - - values["client"] = qianfan.ChatCompletion(**default_values) - return values - - @property - def _llm_type(self) -> str: - return "baidu-qianfan-chat" - - @property - def _default_params(self) -> Dict[str, Any]: - normal_params = { - "model": self.model, - "endpoint": self.endpoint, - "stream": self.streaming, - "request_timeout": self.request_timeout, - "top_p": self.top_p, - "temperature": self.temperature, - "penalty_score": self.penalty_score, - } - return {**normal_params, **self.model_kwargs} - - def _convert_prompt_msg_params(self, messages: List[BaseMessage], **kwargs: Any) -> Dict[str, Any]: - message_params: Dict[str, Any] = { - "messages": [convert_message_to_dict(message) for message in messages if not isinstance(message, SystemMessage)] - } - for message in messages: - if isinstance(message, SystemMessage): - message_params["system"] = f'{message_params.get("system", "")}{message.content}\n' - return {**message_params, **self._default_params, **kwargs} - - def _generate( - self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any, - ) -> ChatResult: - if self.streaming: - completion = "" - usage_metadata: Optional[UsageMetadata] = None - generation_info: Dict[str, Any] = {} - for chunk in self._stream(messages, stop=stop, run_manager=run_manager, **kwargs): - completion += chunk.text - generation_info = chunk.generation_info or generation_info - if isinstance(chunk.message, AIMessageChunk): - usage_metadata = chunk.message.usage_metadata - return ChatResult( - generations=[ - ChatGeneration( - message=AIMessage(content=completion, additional_kwargs={}, usage_metadata=usage_metadata), - generation_info=generation_info or {"finish_reason": "stop"}, - ) - ], - llm_output={"token_usage": usage_metadata or {}, "model_name": self.model}, - ) - - params = self._convert_prompt_msg_params(messages, **kwargs) - params["stop"] = stop - response_payload = self.client.do(**params) - message = _convert_dict_to_message(response_payload) - return ChatResult( - generations=[ - ChatGeneration( - message=message, - generation_info={"finish_reason": "stop", **response_payload.get("body", {})}, - ) - ], - llm_output={ - "token_usage": response_payload.get("usage", response_payload.get("body", {}).get("usage", {})), - "model_name": self.model, - }, - ) - - def _stream( - self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any, - ) -> Iterator[ChatGenerationChunk]: - params = self._convert_prompt_msg_params(messages, **kwargs) - params["stop"] = stop - params["stream"] = True - for response in self.client.do(**params): - if not response: - continue - message = _convert_dict_to_message(response) - function_call = message.additional_kwargs.get("function_call", {}) - chunk = ChatGenerationChunk( - text=response["result"], - message=AIMessageChunk( - content=message.content, - role="assistant", - additional_kwargs=function_call, - usage_metadata=message.usage_metadata, - ), - generation_info=message.additional_kwargs, - ) - if run_manager: - run_manager.on_llm_new_token(chunk.text, chunk=chunk) - yield chunk - - -class QianfanEmbeddingsEndpoint(Embeddings, BaseModel): - qianfan_ak: Optional[SecretStr] = Field(default=None, alias="api_key") - qianfan_sk: Optional[SecretStr] = Field(default=None, alias="secret_key") - chunk_size: int = 16 - model: Optional[str] = Field(default=None) - endpoint: str = "" - client: Any = None - init_kwargs: Dict[str, Any] = Field(default_factory=dict) - model_kwargs: Dict[str, Any] = Field(default_factory=dict) - - model_config = ConfigDict(populate_by_name=True, protected_namespaces=()) - - @model_validator(mode="before") - @classmethod - def validate_environment(cls, values: Dict[str, Any]) -> Dict[str, Any]: - values["qianfan_ak"] = convert_to_secret_str( - get_from_dict_or_env(values, ["qianfan_ak", "api_key"], "QIANFAN_AK", default="") - ) - values["qianfan_sk"] = convert_to_secret_str( - get_from_dict_or_env(values, ["qianfan_sk", "secret_key"], "QIANFAN_SK", default="") - ) - - params = {**values.get("init_kwargs", {}), "model": values.get("model")} - if values["qianfan_ak"].get_secret_value(): - params["ak"] = values["qianfan_ak"].get_secret_value() - if values["qianfan_sk"].get_secret_value(): - params["sk"] = values["qianfan_sk"].get_secret_value() - if values.get("endpoint"): - params["endpoint"] = values["endpoint"] - - import qianfan - - values["client"] = qianfan.Embedding(**params) - return values - - def embed_query(self, text: str) -> List[float]: - return self.embed_documents([text])[0] - - def embed_documents(self, texts: List[str]) -> List[List[float]]: - text_chunks = [texts[i : i + self.chunk_size] for i in range(0, len(texts), self.chunk_size)] - embeddings: List[List[float]] = [] - for chunk in text_chunks: - response = self.client.do(texts=chunk, **self.model_kwargs) - embeddings.extend([item["embedding"] for item in response["data"]]) - return embeddings diff --git a/apps/users/urls.py b/apps/users/urls.py index a43e018475c..b159f23fb15 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -13,7 +13,6 @@ path('user/logout', views.Logout.as_view(), name='logout'), path('user/language', views.SwitchUserLanguageView.as_view(), name='language'), path("user/send_email", views.SendEmail.as_view(), name='send_email'), - path("user/check_code", views.CheckCode.as_view(), name='check_code'), path("user/re_password", views.RePasswordView.as_view(), name='re_password'), path("user/current/send_email", views.SendEmailToCurrentUserView.as_view(), name="send_email_current"), path("user/current/reset_password", views.ResetCurrentUserPasswordView.as_view(), name="reset_password_current"), @@ -33,4 +32,3 @@ path('system/workspace//user_group//remove_member', views.SystemUserGroupView.RemoveMember.as_view()), path('system/workspace//user_group//user_list//', views.SystemUserGroupView.UserList.as_view()), ] - diff --git a/apps/users/views/user.py b/apps/users/views/user.py index b75c7dc0ea5..046d4dec933 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -427,26 +427,6 @@ def post(self, request: Request): return result.success(serializer_obj.send()) -class CheckCode(APIView): - @extend_schema( - methods=["POST"], - summary=_("Check whether the verification code is correct"), - description=_("Check whether the verification code is correct"), - operation_id=_("Check whether the verification code is correct"), # type: ignore - tags=[_("User Management")], # type: ignore - request=CheckCodeAPI.get_request(), - responses=CheckCodeAPI.get_response(), - ) - @log( - menu="User management", - operate="Check whether the verification code is correct", - get_operation_object=lambda r, k: {"name": r.data.get("email", None)}, - get_user=lambda r: {"user_name": None, "email": r.data.get("email", None)}, - ) - def post(self, request: Request): - return result.success(CheckCodeSerializer(data=request.data).is_valid(raise_exception=True)) - - class SendEmailToCurrentUserView(APIView): authentication_classes = [TokenAuth] diff --git a/ui/src/api/admin/auth/forgot-password.ts b/ui/src/api/admin/auth/forgot-password.ts new file mode 100644 index 00000000000..34d00a1334c --- /dev/null +++ b/ui/src/api/admin/auth/forgot-password.ts @@ -0,0 +1,16 @@ +/** 提供 Admin 忘记密码页面发送验证码与重置密码的接口。 */ + +import { post } from '../core/request' +import type { ResetPasswordRequest, SendEmailRequest } from '@/api/types/login' + +/** 向指定邮箱发送用于重置密码的验证码。 */ +const postSendVerificationCode = (email: string) => { + return post('/user/send_email', { email, type: 'reset_password' }) +} + +/** 校验邮箱验证码并重置密码。 */ +const postResetPassword = (request: ResetPasswordRequest) => { + return post('/user/re_password', request) +} + +export default { postResetPassword, postSendVerificationCode } diff --git a/ui/src/api/types/login.ts b/ui/src/api/types/login.ts index 9e0c60bb420..e6cb574db20 100644 --- a/ui/src/api/types/login.ts +++ b/ui/src/api/types/login.ts @@ -25,3 +25,18 @@ export interface UpdatePasswordForm { password: string re_password: string } + +/** 忘记密码页面发送邮箱验证码的请求。 */ +export interface SendEmailRequest { + email: string + type: string +} + +/** 忘记密码页面校验验证码并重置密码的请求。 */ +export interface ResetPasswordRequest { + email: string + code: string + password: string + re_password: string + encrypted?: boolean +} diff --git a/ui/src/components/mk-dynamics-form/items/model/provider-data.ts b/ui/src/components/mk-dynamics-form/items/model/provider-data.ts index 41443219fde..14aad927315 100644 --- a/ui/src/components/mk-dynamics-form/items/model/provider-data.ts +++ b/ui/src/components/mk-dynamics-form/items/model/provider-data.ts @@ -5,7 +5,7 @@ export const providerList = [ icon: '', }, { - provider: 'model_wenxin_provider', + provider: 'model_qianfan_provider', name: '千帆大模型', icon: '\n\n\n\n', }, diff --git a/ui/src/views/login/ForgotPasswordView.vue b/ui/src/views/login/ForgotPasswordView.vue index 35c8fe03f25..0b51f0b150e 100644 --- a/ui/src/views/login/ForgotPasswordView.vue +++ b/ui/src/views/login/ForgotPasswordView.vue @@ -1,7 +1,11 @@ @@ -68,7 +129,9 @@ const handleResetPassword = async () => { - 获取验证码 + + {{ countdown > 0 ? `${countdown}s` : '获取验证码' }} + @@ -80,7 +143,7 @@ const handleResetPassword = async () => { - 修改密码 + 修改密码