diff --git a/api/core/model_runtime/model_providers/_position.yaml b/api/core/model_runtime/model_providers/_position.yaml index c06f122984..bda53e4394 100644 --- a/api/core/model_runtime/model_providers/_position.yaml +++ b/api/core/model_runtime/model_providers/_position.yaml @@ -27,3 +27,4 @@ - openllm - localai - openai_api_compatible +- deepseek diff --git a/api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.png b/api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.png deleted file mode 100644 index 25254d7f53..0000000000 Binary files a/api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.png and /dev/null differ diff --git a/api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.svg b/api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.svg new file mode 100644 index 0000000000..425494404f --- /dev/null +++ b/api/core/model_runtime/model_providers/deepseek/_assets/icon_l_en.svg @@ -0,0 +1,22 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + diff --git a/api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.png b/api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.png deleted file mode 100644 index 3271f5cfe4..0000000000 Binary files a/api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.png and /dev/null differ diff --git a/api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.svg b/api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.svg new file mode 100644 index 0000000000..aa854a7504 --- /dev/null +++ b/api/core/model_runtime/model_providers/deepseek/_assets/icon_s_en.svg @@ -0,0 +1,3 @@ + + + diff --git a/api/core/model_runtime/model_providers/deepseek/deepseek.py b/api/core/model_runtime/model_providers/deepseek/deepseek.py index 5fb821ed45..d61fd4ddc8 100644 --- a/api/core/model_runtime/model_providers/deepseek/deepseek.py +++ b/api/core/model_runtime/model_providers/deepseek/deepseek.py @@ -7,7 +7,8 @@ from core.model_runtime.model_providers.__base.model_provider import ModelProvid logger = logging.getLogger(__name__) -class DeepseekProvider(ModelProvider): + +class DeepSeekProvider(ModelProvider): def validate_provider_credentials(self, credentials: dict) -> None: """ @@ -19,6 +20,8 @@ class DeepseekProvider(ModelProvider): try: model_instance = self.get_model_instance(ModelType.LLM) + # Use `deepseek-chat` model for validate, + # no matter what model you pass in, text completion model or chat model model_instance.validate_credentials( model='deepseek-chat', credentials=credentials diff --git a/api/core/model_runtime/model_providers/deepseek/deepseek.yaml b/api/core/model_runtime/model_providers/deepseek/deepseek.yaml index b535053c36..dacb20ab18 100644 --- a/api/core/model_runtime/model_providers/deepseek/deepseek.yaml +++ b/api/core/model_runtime/model_providers/deepseek/deepseek.yaml @@ -1,15 +1,19 @@ provider: deepseek label: - en_US: Deepseek + en_US: deepseek + zh_Hans: 深度求索 +description: + en_US: Models provided by deepseek, such as deepseek-chat、deepseek-coder. + zh_Hans: 深度求索提供的模型,例如 deepseek-chat、deepseek-coder 。 icon_small: - en_US: icon_s_en.png + en_US: icon_s_en.svg icon_large: - en_US: icon_l_en.png -background: "#FFFFFF" + en_US: icon_l_en.svg +background: "#c0cdff" help: title: - en_US: Get your API Key from Deepseek - zh_Hans: 从 Deepseek 获取 API Key + en_US: Get your API Key from deepseek + zh_Hans: 从深度求索获取 API Key url: en_US: https://platform.deepseek.com/api_keys supported_model_types: @@ -26,3 +30,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key + - variable: endpoint_url + label: + zh_Hans: 自定义 API endpoint 地址 + en_US: CUstom API endpoint URL + type: text-input + required: false + placeholder: + zh_Hans: Base URL, e.g. https://api.deepseek.com/v1 or https://api.deepseek.com + en_US: Base URL, e.g. https://api.deepseek.com/v1 or https://api.deepseek.com diff --git a/api/core/model_runtime/model_providers/deepseek/llm/deepseek-chat.yaml b/api/core/model_runtime/model_providers/deepseek/llm/deepseek-chat.yaml index a766a09a7b..3a5a63fa61 100644 --- a/api/core/model_runtime/model_providers/deepseek/llm/deepseek-chat.yaml +++ b/api/core/model_runtime/model_providers/deepseek/llm/deepseek-chat.yaml @@ -11,16 +11,54 @@ model_properties: parameter_rules: - name: temperature use_template: temperature - min: 0 - max: 1 - default: 0.5 - - name: top_p - use_template: top_p - min: 0 - max: 1 + type: float default: 1 + min: 0.0 + max: 2.0 + help: + zh_Hans: 控制生成结果的多样性和随机性。数值越小,越严谨;数值越大,越发散。 + en_US: Control the diversity and randomness of generated results. The smaller the value, the more rigorous it is; the larger the value, the more divergent it is. - name: max_tokens use_template: max_tokens + type: int + default: 4096 min: 1 max: 32000 - default: 1024 + help: + zh_Hans: 指定生成结果长度的上限。如果生成结果截断,可以调大该参数。 + en_US: Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter. + - name: top_p + use_template: top_p + type: float + default: 1 + min: 0.01 + max: 1.00 + help: + zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 + en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. + - name: logprobs + help: + zh_Hans: 是否返回所输出 token 的对数概率。如果为 true,则在 message 的 content 中返回每个输出 token 的对数概率。 + en_US: Whether to return the log probability of the output token. If true, returns the log probability of each output token in the content of message . + type: boolean + - name: top_logprobs + type: int + default: 0 + min: 0 + max: 20 + help: + zh_Hans: 一个介于 0 到 20 之间的整数 N,指定每个输出位置返回输出概率 top N 的 token,且返回这些 token 的对数概率。指定此参数时,logprobs 必须为 true。 + en_US: An integer N between 0 and 20, specifying that each output position returns the top N tokens with output probability, and returns the logarithmic probability of these tokens. When specifying this parameter, logprobs must be true. + - name: frequency_penalty + use_template: frequency_penalty + default: 0 + min: -2.0 + max: 2.0 + help: + zh_Hans: 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。 + en_US: A number between -2.0 and 2.0. If the value is positive, new tokens are penalized based on their frequency of occurrence in existing text, reducing the likelihood that the model will repeat the same content. +pricing: + input: '1' + output: '2' + unit: '0.000001' + currency: RMB diff --git a/api/core/model_runtime/model_providers/deepseek/llm/llm.py b/api/core/model_runtime/model_providers/deepseek/llm/llm.py index 4d20a07447..bdb3823b60 100644 --- a/api/core/model_runtime/model_providers/deepseek/llm/llm.py +++ b/api/core/model_runtime/model_providers/deepseek/llm/llm.py @@ -1,18 +1,24 @@ from collections.abc import Generator from typing import Optional, Union +from urllib.parse import urlparse + +import tiktoken from core.model_runtime.entities.llm_entities import LLMResult -from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool -from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel +from core.model_runtime.entities.message_entities import ( + PromptMessage, + PromptMessageTool, +) +from core.model_runtime.model_providers.openai.llm.llm import OpenAILargeLanguageModel -class DeepseekLargeLanguageModel(OAIAPICompatLargeLanguageModel): +class DeepSeekLargeLanguageModel(OpenAILargeLanguageModel): + def _invoke(self, model: str, credentials: dict, prompt_messages: list[PromptMessage], model_parameters: dict, tools: Optional[list[PromptMessageTool]] = None, stop: Optional[list[str]] = None, stream: bool = True, user: Optional[str] = None) \ -> Union[LLMResult, Generator]: - self._add_custom_parameters(credentials) return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) @@ -21,7 +27,87 @@ class DeepseekLargeLanguageModel(OAIAPICompatLargeLanguageModel): self._add_custom_parameters(credentials) super().validate_credentials(model, credentials) + + # refactored from openai model runtime, use cl100k_base for calculate token number + def _num_tokens_from_string(self, model: str, text: str, + tools: Optional[list[PromptMessageTool]] = None) -> int: + """ + Calculate num tokens for text completion model with tiktoken package. + + :param model: model name + :param text: prompt text + :param tools: tools for tool calling + :return: number of tokens + """ + encoding = tiktoken.get_encoding("cl100k_base") + num_tokens = len(encoding.encode(text)) + + if tools: + num_tokens += self._num_tokens_for_tools(encoding, tools) + + return num_tokens + + # refactored from openai model runtime, use cl100k_base for calculate token number + def _num_tokens_from_messages(self, model: str, messages: list[PromptMessage], + tools: Optional[list[PromptMessageTool]] = None) -> int: + """Calculate num tokens for gpt-3.5-turbo and gpt-4 with tiktoken package. + + Official documentation: https://github.com/openai/openai-cookbook/blob/ + main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb""" + encoding = tiktoken.get_encoding("cl100k_base") + tokens_per_message = 3 + tokens_per_name = 1 + + num_tokens = 0 + messages_dict = [self._convert_prompt_message_to_dict(m) for m in messages] + for message in messages_dict: + num_tokens += tokens_per_message + for key, value in message.items(): + # Cast str(value) in case the message value is not a string + # This occurs with function messages + # TODO: The current token calculation method for the image type is not implemented, + # which need to download the image and then get the resolution for calculation, + # and will increase the request delay + if isinstance(value, list): + text = '' + for item in value: + if isinstance(item, dict) and item['type'] == 'text': + text += item['text'] + + value = text + + if key == "tool_calls": + for tool_call in value: + for t_key, t_value in tool_call.items(): + num_tokens += len(encoding.encode(t_key)) + if t_key == "function": + for f_key, f_value in t_value.items(): + num_tokens += len(encoding.encode(f_key)) + num_tokens += len(encoding.encode(f_value)) + else: + num_tokens += len(encoding.encode(t_key)) + num_tokens += len(encoding.encode(t_value)) + else: + num_tokens += len(encoding.encode(str(value))) + + if key == "name": + num_tokens += tokens_per_name + + # every reply is primed with assistant + num_tokens += 3 + + if tools: + num_tokens += self._num_tokens_for_tools(encoding, tools) + + return num_tokens + @staticmethod def _add_custom_parameters(credentials: dict) -> None: credentials['mode'] = 'chat' - credentials['endpoint_url'] = 'https://api.deepseek.com/' + credentials['openai_api_key']=credentials['api_key'] + if 'endpoint_url' not in credentials or credentials['endpoint_url'] == "": + credentials['openai_api_base']='https://api.deepseek.com' + else: + parsed_url = urlparse(credentials['endpoint_url']) + credentials['openai_api_base']=f"{parsed_url.scheme}://{parsed_url.netloc}" +