Skip to content

Commit

Permalink
Fix Deepseek Function/Tool Calling (langgenius#11023)
Browse files Browse the repository at this point in the history
  • Loading branch information
taowang1993 authored Nov 25, 2024
1 parent 87c831e commit aae29e7
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ label:
model_type: llm
features:
- agent-thought
- tool-call
- multi-tool-call
- stream-tool-call
model_properties:
Expand Down Expand Up @@ -72,7 +73,7 @@ parameter_rules:
- text
- json_object
pricing:
input: '1'
output: '2'
unit: '0.000001'
input: "1"
output: "2"
unit: "0.000001"
currency: RMB
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ label:
model_type: llm
features:
- agent-thought
- tool-call
- multi-tool-call
- stream-tool-call
model_properties:
Expand Down
98 changes: 10 additions & 88 deletions api/core/model_runtime/model_providers/deepseek/llm/llm.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
from collections.abc import Generator
from typing import Optional, Union
from urllib.parse import urlparse

import tiktoken
from yarl import URL

from core.model_runtime.entities.llm_entities import LLMResult
from core.model_runtime.entities.llm_entities import LLMMode, LLMResult
from core.model_runtime.entities.message_entities import (
PromptMessage,
PromptMessageTool,
)
from core.model_runtime.model_providers.openai.llm.llm import OpenAILargeLanguageModel
from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAIAPICompatLargeLanguageModel


class DeepSeekLargeLanguageModel(OpenAILargeLanguageModel):
class DeepseekLargeLanguageModel(OAIAPICompatLargeLanguageModel):
def _invoke(
self,
model: str,
Expand All @@ -25,92 +24,15 @@ def _invoke(
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)
return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream)

def validate_credentials(self, model: str, credentials: dict) -> None:
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 <im_start>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["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}"
def _add_custom_parameters(credentials) -> None:
credentials["endpoint_url"] = str(URL(credentials.get("endpoint_url", "https://api.deepseek.com")))
credentials["mode"] = LLMMode.CHAT.value
credentials["function_calling_type"] = "tool_call"
credentials["stream_function_calling"] = "support"

0 comments on commit aae29e7

Please sign in to comment.