# SPDX-License-Identifier: Apache-2.0 """vLLM reasoning parser for Aria v1's legacy think-tag format. Aria v1 was trained with a prompt-prefilled ```` tag and a generated ```` delimiter before the final answer. Gemma's tokenizer does not represent these strings as single special tokens, so vLLM's built-in ``qwen3``/``deepseek_r1`` parsers cannot be used directly. Usage: vllm serve xlr8harder/aria-gemma4-31b-v1 \ --reasoning-parser-plugin ./aria_v1_reasoning_parser.py \ --reasoning-parser aria_v1 """ from __future__ import annotations from collections.abc import Iterable, Sequence from vllm.entrypoints.openai.engine.protocol import DeltaMessage from vllm.reasoning.abs_reasoning_parsers import ( ReasoningParser, ReasoningParserManager, ) THINK_OPEN = "" THINK_CLOSE = "" def _find_subsequence(haystack: Sequence[int], needle: Sequence[int]) -> int: if not needle: return -1 limit = len(haystack) - len(needle) + 1 for i in range(max(0, limit)): if list(haystack[i : i + len(needle)]) == list(needle): return i return -1 def _suffix_prefix_len(text: str, prefix: str) -> int: max_len = min(len(text), len(prefix) - 1) for length in range(max_len, 0, -1): if text[-length:] == prefix[:length]: return length return 0 @ReasoningParserManager.register_module("aria_v1") class AriaV1ReasoningParser(ReasoningParser): """Parser for outputs shaped as ``reasoninganswer``.""" @property def reasoning_start_str(self) -> str | None: return THINK_OPEN @property def reasoning_end_str(self) -> str | None: return THINK_CLOSE def __init__(self, tokenizer, *args, **kwargs): super().__init__(tokenizer, *args, **kwargs) self.end_token_ids = tokenizer.encode(THINK_CLOSE, add_special_tokens=False) self.open_token_ids = tokenizer.encode(THINK_OPEN, add_special_tokens=False) self._streaming_reasoning_done = False self._streaming_buffer = "" self._streaming_started = False self._strip_next_content_delta = False def _strip_optional_open(self, text: str) -> str: text = text.lstrip() if text.startswith(THINK_OPEN): return text[len(THINK_OPEN) :].lstrip("\r\n ") return text def is_reasoning_end(self, input_ids: Sequence[int]) -> bool: return _find_subsequence(input_ids, self.end_token_ids) >= 0 def is_reasoning_end_streaming( self, input_ids: Sequence[int], delta_ids: Iterable[int] ) -> bool: return self.is_reasoning_end(input_ids) def extract_content_ids(self, input_ids: list[int]) -> list[int]: end_index = _find_subsequence(input_ids, self.end_token_ids) if end_index < 0: return [] return input_ids[end_index + len(self.end_token_ids) :] def count_reasoning_tokens(self, token_ids: Sequence[int]) -> int: end_index = _find_subsequence(token_ids, self.end_token_ids) return len(token_ids) if end_index < 0 else end_index def extract_reasoning(self, model_output: str, request) -> tuple[str | None, str | None]: model_output = self._strip_optional_open(model_output) if THINK_CLOSE not in model_output: return model_output, None reasoning, _, content = model_output.partition(THINK_CLOSE) return reasoning.strip(), content.lstrip("\r\n ") or None def extract_reasoning_streaming( self, previous_text: str, current_text: str, delta_text: str, previous_token_ids: Sequence[int], current_token_ids: Sequence[int], delta_token_ids: Sequence[int], ) -> DeltaMessage | None: if self._streaming_reasoning_done: if self._strip_next_content_delta: delta_text = delta_text.lstrip("\r\n ") if delta_text: self._strip_next_content_delta = False else: return None return DeltaMessage(content=delta_text) if delta_text else None combined = self._streaming_buffer + delta_text if not self._streaming_started: combined = self._strip_optional_open(combined) self._streaming_started = True if THINK_CLOSE in combined: reasoning, _, content = combined.partition(THINK_CLOSE) self._streaming_buffer = "" self._streaming_reasoning_done = True stripped_content = content.lstrip("\r\n ") if not stripped_content: self._strip_next_content_delta = True return DeltaMessage( reasoning=reasoning if reasoning else None, content=stripped_content or None, ) keep = _suffix_prefix_len(combined, THINK_CLOSE) if keep: emit = combined[:-keep] self._streaming_buffer = combined[-keep:] else: emit = combined self._streaming_buffer = "" return DeltaMessage(reasoning=emit) if emit else None