Source code for kani.model_specific.deepseek
import json
import logging
import re
from kani.models import FunctionCall, ToolCall
from .base import BaseParser
log = logging.getLogger(__name__)
[docs]
class DeepSeekR1Parser(BaseParser):
r"""
Tool calling adapter for DeepSeek models using the R1 tool call format::
deepseek-ai/DeepSeek-R1
deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
deepseek-ai/DeepSeek-R1-Distill-Llama-8B
deepseek-ai/DeepSeek-R1-Distill-Qwen-14B
deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
deepseek-ai/DeepSeek-R1-Distill-Llama-70B
Reasoning segments are returned as :class:`.ReasoningPart`\ s.
"""
def __init__(
self,
*args,
tool_call_start_token: str = "<|tool▁calls▁begin|>",
tool_call_end_token: str = "<|tool▁outputs▁end|>",
reasoning_start_token: str | None = "<think>",
reasoning_end_token: str | None = "</think>",
reasoning_always_at_start=True,
**kwargs,
):
super().__init__(
*args,
tool_call_start_token=tool_call_start_token,
tool_call_end_token=tool_call_end_token,
reasoning_start_token=reasoning_start_token,
reasoning_end_token=reasoning_end_token,
reasoning_always_at_start=reasoning_always_at_start,
**kwargs,
)
def parse_tool_calls(self, content: str):
tool_calls = []
tool_content_match = re.search(
rf"{re.escape(self.tool_call_start_token)}\s*(.+?)\s*({re.escape(self.tool_call_end_token)})",
content,
re.IGNORECASE | re.DOTALL,
)
if tool_content_match:
content = content[: tool_content_match.start()]
log.debug(f"Found tool content while parsing: {tool_content_match.group(1)}")
# translate to kani spec
for tc_match in re.finditer(
r"<|tool▁call▁begin|>(?P<type>.+?)<|tool▁sep|>(?P<name>.+?)\n"
r"```json\n(?P<args>.+?)\n```<|tool▁call▁end|>",
tool_content_match.group(1),
re.IGNORECASE | re.DOTALL,
):
tool_name = tc_match["name"].strip()
tool_args = tc_match["args"].strip()
try:
json.loads(tool_args)
except json.JSONDecodeError:
log.error(
f"Could not decode tool content! Skipping this tool call:\n{tc_match[0]!r}!", exc_info=True
)
continue
tool_call = ToolCall.from_function_call(FunctionCall(name=tool_name, arguments=tool_args))
tool_calls.append(tool_call)
# return trimmed content and tool calls
return content, tool_calls
DeepSeekR1ToolCallParser = DeepSeekR1Parser
# ===== deepseek-r1 function calling =====
# {% if not add_generation_prompt is defined %}
# {% set add_generation_prompt = false %}
# {% endif %}
# {% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='', is_first_sp=true) %}
# {%- for message in messages %}
# {%- if message['role'] == 'system' %}
# {%- if ns.is_first_sp %}
# {% set ns.system_prompt = ns.system_prompt + message['content'] %}
# {% set ns.is_first_sp = false %}
# {%- else %}
# {% set ns.system_prompt = ns.system_prompt + '\\n\\n' + message['content'] %}
# {%- endif %}
# {%- endif %}
# {%- endfor %}
# {{ bos_token }}{{ ns.system_prompt }}
# {%- for message in messages %}
# {%- if message['role'] == 'user' %}
# {%- set ns.is_tool = false -%}
# {{'<|User|>' + message['content']}}
# {%- endif %}
# {%- if message['role'] == 'assistant' and 'tool_calls' in message %}
# {%- set ns.is_tool = false -%}
# {%- for tool in message['tool_calls'] %}
# {%- if not ns.is_first %}
# {%- if message['content'] is none %}
# {{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}
# {%- else %}
# {{'<|Assistant|>' + message['content'] + '<|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}
# {%- endif %}
# {%- set ns.is_first = true -%}
# {%- else %}
# {{'\\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}
# {%- endif %}
# {%- endfor %}
# {{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}
# {%- endif %}
# {%- if message['role'] == 'assistant' and 'tool_calls' not in message %}
# {%- if ns.is_tool %}
# {{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}
# {%- set ns.is_tool = false -%}
# {%- else %}
# {% set content = message['content'] %}
# {% if '</think>' in content %}
# {% set content = content.split('</think>')[-1] %}
# {% endif %}
# {{'<|Assistant|>' + content + '<|end▁of▁sentence|>'}}
# {%- endif %}
# {%- endif %}
# {%- if message['role'] == 'tool' %}
# {%- set ns.is_tool = true -%}
# {%- if ns.is_output_first %}
# {{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}
# {%- set ns.is_output_first = false %}
# {%- else %}
# {{'<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}
# {%- endif %}
# {%- endif %}
# {%- endfor -%}
# {% if ns.is_tool %}
# {{'<|tool▁outputs▁end|>'}}
# {% endif %}
# {% if add_generation_prompt and not ns.is_tool %}
# {{'<|Assistant|>'}}
# {% endif %}