From 5695666eaed1b3b8a7aeec81bb1ae36e5b6503e1 Mon Sep 17 00:00:00 2001 From: Eddie Zhou Date: Thu, 30 Oct 2025 15:52:04 -0700 Subject: [PATCH 1/3] Fix Qwen rendering --- tinker_cookbook/renderers.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tinker_cookbook/renderers.py b/tinker_cookbook/renderers.py index 5e6c876..1740796 100644 --- a/tinker_cookbook/renderers.py +++ b/tinker_cookbook/renderers.py @@ -337,6 +337,9 @@ def _render_message(self, idx: int, message: Message) -> tuple[list[int], list[i ob_str += "\n" # Observation (prompt) part ac_str = f"{ac_content}<|im_end|>" + if "tool_calls" in message: + ac_str += "\n".join([f"\n{json.dumps(tool_call)}\n" for tool_call in message["tool_calls"]]) + ac_str += "<|im_end|>" # Action part ac_tail_str = "" # No action tail needed for Qwen format # Action part that's only included in the last message in SFT @@ -405,10 +408,11 @@ def parse_response(self, response: list[int]) -> tuple[Message, bool]: if not parse_success: return assistant_message, False - # NOTE: - # we use the ... tag to wrap the tool call. + # Follow Qwen docs and Qwen-Agent's tool calling prompt to use ... tags to wrap the tool call. + # - https://qwen.readthedocs.io/en/latest/getting_started/concepts.html#tool-calling + # - https://github.com/QwenLM/Qwen-Agent/blob/main/qwen_agent/llm/fncall_prompts/nous_fncall_prompt.py#L279-L282 match = re.search( - r"(.*?)", assistant_message["content"], re.DOTALL + r"(.*?)", assistant_message["content"], re.DOTALL ) if match: tool_calls = self._parse_tool_call(match.group(1)) From ef123a540371ad05736bd857bc113d51b1354cd4 Mon Sep 17 00:00:00 2001 From: Eddie Zhou Date: Thu, 30 Oct 2025 16:01:46 -0700 Subject: [PATCH 2/3] precommit --- tinker_cookbook/renderers.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tinker_cookbook/renderers.py b/tinker_cookbook/renderers.py index 1740796..1852fe8 100644 --- a/tinker_cookbook/renderers.py +++ b/tinker_cookbook/renderers.py @@ -338,7 +338,12 @@ def _render_message(self, idx: int, message: Message) -> tuple[list[int], list[i # Observation (prompt) part ac_str = f"{ac_content}<|im_end|>" if "tool_calls" in message: - ac_str += "\n".join([f"\n{json.dumps(tool_call)}\n" for tool_call in message["tool_calls"]]) + ac_str += "\n".join( + [ + f"\n{json.dumps(tool_call)}\n" + for tool_call in message["tool_calls"] + ] + ) ac_str += "<|im_end|>" # Action part ac_tail_str = "" # No action tail needed for Qwen format @@ -411,9 +416,7 @@ def parse_response(self, response: list[int]) -> tuple[Message, bool]: # Follow Qwen docs and Qwen-Agent's tool calling prompt to use ... tags to wrap the tool call. # - https://qwen.readthedocs.io/en/latest/getting_started/concepts.html#tool-calling # - https://github.com/QwenLM/Qwen-Agent/blob/main/qwen_agent/llm/fncall_prompts/nous_fncall_prompt.py#L279-L282 - match = re.search( - r"(.*?)", assistant_message["content"], re.DOTALL - ) + match = re.search(r"(.*?)", assistant_message["content"], re.DOTALL) if match: tool_calls = self._parse_tool_call(match.group(1)) if tool_calls is None: From a2296983857ef84793d9d76027090defd8a4858b Mon Sep 17 00:00:00 2001 From: Eddie Zhou Date: Thu, 30 Oct 2025 16:05:27 -0700 Subject: [PATCH 3/3] bad merge --- tinker_cookbook/renderers.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tinker_cookbook/renderers.py b/tinker_cookbook/renderers.py index 1852fe8..2e87b6b 100644 --- a/tinker_cookbook/renderers.py +++ b/tinker_cookbook/renderers.py @@ -336,21 +336,20 @@ def _render_message(self, idx: int, message: Message) -> tuple[list[int], list[i # in the assistant messages, we so don't need to re-add it in those cases. ob_str += "\n" # Observation (prompt) part - ac_str = f"{ac_content}<|im_end|>" if "tool_calls" in message: - ac_str += "\n".join( + ac_content += "\n".join( [ f"\n{json.dumps(tool_call)}\n" for tool_call in message["tool_calls"] ] ) - ac_str += "<|im_end|>" + ac_content += "<|im_end|>" # Action part ac_tail_str = "" # No action tail needed for Qwen format # Action part that's only included in the last message in SFT return ( self.tokenizer.encode(ob_str, add_special_tokens=False), - self.tokenizer.encode(ac_str, add_special_tokens=False), + self.tokenizer.encode(ac_content, add_special_tokens=False), self.tokenizer.encode(ac_tail_str, add_special_tokens=False), )