diff --git a/src/engine/ov_genai/qwen_tool_parser.py b/src/engine/ov_genai/qwen_tool_parser.py index 9169110..6c52378 100644 --- a/src/engine/ov_genai/qwen_tool_parser.py +++ b/src/engine/ov_genai/qwen_tool_parser.py @@ -221,7 +221,7 @@ def _step(self, content_out: list, fragments: list) -> bool: self.status = StreamingStatus.TOOL_CALL_STOP return True self._buf = buf[len(TOOL_OPEN):] - self._start_call(fragments) + self._start_call() self._state = IN_TOOL_CALL return True @@ -238,11 +238,7 @@ def _step(self, content_out: list, fragments: list) -> bool: return False name = buf[:gt].strip() self._buf = buf[gt + 1:] - self._cur["function"]["name"] = name - fragments.append({ - "index": len(self._calls) - 1, - "function": {"name": name}, - }) + self._register_call(name, fragments) self._state = IN_FUNCTION return True @@ -332,19 +328,28 @@ def _frag(self, fragments: list, arguments: str): }) self._cur["function"]["arguments"] += arguments - def _start_call(self, fragments: list): + def _start_call(self): + # Pending only: not registered in `_calls` (and no fragment emitted) + # until `_register_call` confirms this is really Qwen's + # syntax. A block using a different + # format (e.g. Hermes-style raw JSON) never registers, so it can't + # surface as a bogus half-empty tool call - it's left for the + # accumulated-text Hermes fallback in openai.py to pick up instead. self._cur = { "id": f"call_{next(self._ids):024x}", "type": "function", "function": {"name": "", "arguments": ""}, } - self._calls.append(self._cur) self._param_index = 0 + + def _register_call(self, name: str, fragments: list): + self._cur["function"]["name"] = name + self._calls.append(self._cur) fragments.append({ "index": len(self._calls) - 1, "id": self._cur["id"], "type": "function", - "function": {"name": "", "arguments": ""}, + "function": {"name": name, "arguments": ""}, }) def _close_call(self, fragments: list): diff --git a/src/server/routes/openai.py b/src/server/routes/openai.py index cd923dd..da01f86 100644 --- a/src/server/routes/openai.py +++ b/src/server/routes/openai.py @@ -292,11 +292,10 @@ async def openai_chat_completions( created_ts = int(time.time()) request_id = f"ov-{uuid.uuid4().hex[:24]}" - thinking_enabled = True - if chat_template_kwargs: - thinking_enabled = chat_template_kwargs.get( - "enable_thinking", True - ) + # Don't assume hybrid-thinking mode unless told to expect it (see PR + # description): models without a closing tag would otherwise + # have their entire streamed answer misclassified as reasoning_content. + thinking_enabled = bool(chat_template_kwargs.get("enable_thinking", False)) if generation_config.stream: