-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(deepseek): update reasoner model for V3.2 tool call support #3815
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -49,14 +49,13 @@ | |||||
|
|
||||||
| logger = get_logger(__name__) | ||||||
|
|
||||||
| REASONSER_UNSUPPORTED_PARAMS = [ | ||||||
| REASONER_UNSUPPORTED_PARAMS = [ | ||||||
| "temperature", | ||||||
| "top_p", | ||||||
| "presence_penalty", | ||||||
| "frequency_penalty", | ||||||
| "logprobs", | ||||||
| "top_logprobs", | ||||||
| "tools", | ||||||
| ] | ||||||
|
|
||||||
|
|
||||||
|
|
@@ -124,32 +123,97 @@ def __init__( | |||||
| max_retries=max_retries, | ||||||
| **kwargs, | ||||||
| ) | ||||||
| # Store the last reasoning_content from model response. | ||||||
| # For DeepSeek reasoner models with tool calls, the | ||||||
| # reasoning_content must be passed back in subsequent requests. | ||||||
| self._last_reasoning_content: Optional[str] = None | ||||||
|
|
||||||
| def _inject_reasoning_content( | ||||||
| self, | ||||||
| messages: List[OpenAIMessage], | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit:
Suggested change
|
||||||
| ) -> List[OpenAIMessage]: | ||||||
| r"""Inject the last reasoning_content into assistant messages. | ||||||
|
|
||||||
| For DeepSeek reasoner models with tool call support, the | ||||||
| reasoning_content from the model response needs to be passed back | ||||||
| in subsequent requests for proper context management. | ||||||
|
|
||||||
| Args: | ||||||
| messages: The original messages list. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| Returns: | ||||||
| Messages with reasoning_content added to the last assistant | ||||||
| message that has tool_calls. | ||||||
| """ | ||||||
| if not self._last_reasoning_content: | ||||||
| return messages | ||||||
|
|
||||||
| # Find the last assistant message with tool_calls and inject | ||||||
| # reasoning_content | ||||||
| processed: List[OpenAIMessage] = [] | ||||||
| reasoning_injected = False | ||||||
|
|
||||||
| for msg in reversed(messages): | ||||||
| if ( | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe we can separate these checkings to make it clearer? |
||||||
| not reasoning_injected | ||||||
| and isinstance(msg, dict) | ||||||
| and msg.get("role") == "assistant" | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any enum for the assistant we can use here? |
||||||
| and msg.get("tool_calls") | ||||||
| and "reasoning_content" not in msg | ||||||
| ): | ||||||
| new_msg = dict(msg) | ||||||
| new_msg["reasoning_content"] = self._last_reasoning_content | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be not |
||||||
| processed.append(new_msg) # type: ignore[arg-type] | ||||||
| reasoning_injected = True | ||||||
| else: | ||||||
| processed.append(msg) | ||||||
|
|
||||||
| if reasoning_injected: | ||||||
| self._last_reasoning_content = None | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move it below the |
||||||
|
|
||||||
| return list(reversed(processed)) | ||||||
|
|
||||||
| def _extract_reasoning_content( | ||||||
| self, response: ChatCompletion | ||||||
| ) -> Optional[str]: | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| r"""Extract reasoning_content from the model response. | ||||||
|
|
||||||
| Args: | ||||||
| response: The model response. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| Returns: | ||||||
| The reasoning_content if available, None otherwise. | ||||||
| """ | ||||||
| if response.choices: | ||||||
| return getattr( | ||||||
| response.choices[0].message, "reasoning_content", None | ||||||
| ) | ||||||
| return None | ||||||
|
|
||||||
| def _prepare_request( | ||||||
| self, | ||||||
| messages: List[OpenAIMessage], | ||||||
| response_format: Optional[Type[BaseModel]] = None, | ||||||
| tools: Optional[List[Dict[str, Any]]] = None, | ||||||
| ) -> Dict[str, Any]: | ||||||
| request_config = self.model_config_dict.copy() | ||||||
| import copy | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move to the top |
||||||
|
|
||||||
| request_config = copy.deepcopy(self.model_config_dict) | ||||||
|
|
||||||
| if self.model_type in [ | ||||||
| ModelType.DEEPSEEK_REASONER, | ||||||
| ]: | ||||||
| logger.warning( | ||||||
| "Warning: You are using an DeepSeek Reasoner model, " | ||||||
| "Warning: You are using a DeepSeek Reasoner model, " | ||||||
| "which has certain limitations, reference: " | ||||||
| "`https://api-docs.deepseek.com/guides/reasoning_model" | ||||||
| "`https://api-docs.deepseek.com/guides/thinking_mode" | ||||||
| "#api-parameters`.", | ||||||
| ) | ||||||
| request_config = { | ||||||
| key: value | ||||||
| for key, value in request_config.items() | ||||||
| if key not in REASONSER_UNSUPPORTED_PARAMS | ||||||
| if key not in REASONER_UNSUPPORTED_PARAMS | ||||||
| } | ||||||
| import copy | ||||||
|
|
||||||
| request_config = copy.deepcopy(self.model_config_dict) | ||||||
| # Remove strict from each tool's function parameters since DeepSeek | ||||||
| # does not support them | ||||||
| if tools: | ||||||
|
|
@@ -183,6 +247,10 @@ def _run( | |||||
| """ | ||||||
| self._log_and_trace() | ||||||
|
|
||||||
| # Inject reasoning_content for reasoner tool call continuations | ||||||
| if self.model_type in [ModelType.DEEPSEEK_REASONER]: | ||||||
| messages = self._inject_reasoning_content(messages) | ||||||
|
|
||||||
| request_config = self._prepare_request( | ||||||
| messages, response_format, tools | ||||||
| ) | ||||||
|
|
@@ -193,6 +261,12 @@ def _run( | |||||
| **request_config, | ||||||
| ) | ||||||
|
|
||||||
| # Extract and store reasoning_content for next request | ||||||
| if isinstance(response, ChatCompletion): | ||||||
| self._last_reasoning_content = self._extract_reasoning_content( | ||||||
| response | ||||||
| ) | ||||||
|
|
||||||
| return response | ||||||
|
|
||||||
| @observe() | ||||||
|
|
@@ -215,6 +289,10 @@ async def _arun( | |||||
| """ | ||||||
| self._log_and_trace() | ||||||
|
|
||||||
| # Inject reasoning_content for reasoner tool call continuations | ||||||
| if self.model_type in [ModelType.DEEPSEEK_REASONER]: | ||||||
| messages = self._inject_reasoning_content(messages) | ||||||
|
|
||||||
| request_config = self._prepare_request( | ||||||
| messages, response_format, tools | ||||||
| ) | ||||||
|
|
@@ -224,4 +302,10 @@ async def _arun( | |||||
| **request_config, | ||||||
| ) | ||||||
|
|
||||||
| # Extract and store reasoning_content for next request | ||||||
| if isinstance(response, ChatCompletion): | ||||||
| self._last_reasoning_content = self._extract_reasoning_content( | ||||||
| response | ||||||
| ) | ||||||
|
|
||||||
| return response | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also add a TODO to improve the reasoning content storage
also nit: