Overview
MaybeThinkParser is a flexible parser that extracts content after </think> tags if present, or returns the text unchanged if no think tags exist. This makes it suitable for models that may optionally include reasoning tags.
Class Signature
class MaybeThinkParser(Parser):
def __init__(self, extract_fn: Callable[[str], str] = lambda x: x)
Parameters
Optional extraction function to further process the parsed text. For example, you can use this to extract boxed answers from math problems.
Methods
parse
Extracts content after the last </think> tag, or returns the text unchanged.
def parse(self, text: str) -> str
The text to parse, potentially containing <think>...</think> tags
Returns: The content after the last </think> tag if present, otherwise the original text. The result is passed through extract_fn if provided.
Behavior:
- If
</think> is found: Returns everything after the last </think> tag (stripped)
- If
</think> is NOT found: Returns the original text unchanged (stripped)
- Always applies the
extract_fn to the result
Usage Examples
import verifiers as vf
parser = vf.MaybeThinkParser()
# Parse text with think tags
text = "<think>This is a test string with thinking tags</think> This is the final answer"
result = parser.parse(text)
print(result) # "This is the final answer"
import verifiers as vf
parser = vf.MaybeThinkParser()
# Parse text without think tags
text = "This is a test string without thinking tags"
result = parser.parse(text)
print(result) # "This is a test string without thinking tags"
import re
import verifiers as vf
def extract_boxed(text: str) -> str:
"""Extract content from \boxed{...}"""
match = re.search(r'\\boxed\{([^}]+)\}', text)
return match.group(1) if match else text
parser = vf.MaybeThinkParser(extract_fn=extract_boxed)
# With think tags
text1 = "<think>Reasoning here</think>The answer is \\boxed{42}"
result1 = parser.parse(text1)
print(result1) # "42"
# Without think tags
text2 = "The answer is \\boxed{42}"
result2 = parser.parse(text2)
print(result2) # "42"
Parsing Message Completions
import verifiers as vf
parser = vf.MaybeThinkParser()
# With think tags
messages_with_think = [
{"role": "user", "content": "What is 2+2?"},
{
"role": "assistant",
"content": "<think>This is a test string with thinking tags</think>The answer is 4",
},
]
result = parser.parse_answer(messages_with_think)
print(result) # "The answer is 4"
# Without think tags
messages_without_think = [
{"role": "user", "content": "What is 2+2?"},
{"role": "assistant", "content": "The answer is 4"},
]
result = parser.parse_answer(messages_without_think)
print(result) # "The answer is 4"
In a Math Rubric
import re
import verifiers as vf
def extract_boxed_answer(text: str) -> str:
"""Extract the final answer from \boxed{...}"""
match = re.search(r'\\boxed\{([^}]+)\}', text)
return match.group(1) if match else text
def check_math_correctness(completion, answer, parser, **kwargs):
"""Check if the parsed mathematical answer is correct"""
parsed = parser.parse_answer(completion)
return 1.0 if parsed == str(answer) else 0.0
parser = vf.MaybeThinkParser(extract_fn=extract_boxed_answer)
rubric = vf.Rubric(
funcs=[check_math_correctness],
weights=[1.0],
parser=parser
)
Handling Edge Cases
import verifiers as vf
parser = vf.MaybeThinkParser()
# Empty content after think tag
text1 = "<think>Some thinking</think>"
result1 = parser.parse(text1)
print(result1) # "" (empty string)
# Multiple think blocks
text2 = "<think>First</think>Middle<think>Second</think>Final"
result2 = parser.parse(text2)
print(result2) # "Final" (after last </think>)
# Whitespace handling
text3 = "<think>Thinking</think> Answer with spaces "
result3 = parser.parse(text3)
print(result3) # "Answer with spaces" (stripped)
When to Use MaybeThinkParser
Use MaybeThinkParser when:
- Your model may or may not include
<think>...</think> tags
- You want flexible parsing that works with both reasoning and non-reasoning models
- You’re working with models that sometimes generate think tags (e.g., during fine-tuning)
- You need graceful handling when think tags are absent
Key Differences from ThinkParser:
| Feature | MaybeThinkParser | ThinkParser |
|---|
| Missing think tags | Returns original text | Returns empty string |
| Enforcement | Permissive | Strict |
| Use case | Optional reasoning | Required reasoning |
| Format validation | Not provided | get_format_reward_func() |
Common Use Cases
Math Problem Solving
MaybeThinkParser is commonly used with math rubrics since it can handle both reasoning and direct answers:
import verifiers as vf
from verifiers.utils.math_utils import extract_boxed_answer
parser = vf.MaybeThinkParser(extract_fn=extract_boxed_answer)
# Works with reasoning
text1 = "<think>Let me solve this</think>The answer is \\boxed{42}"
print(parser.parse(text1)) # "42"
# Works without reasoning
text2 = "The answer is \\boxed{42}"
print(parser.parse(text2)) # "42"
Fine-tuning Scenarios
During fine-tuning, models may transition from always using think tags to using them selectively:
import verifiers as vf
parser = vf.MaybeThinkParser()
# Early in training: includes think tags
early_response = "<think>Reasoning step by step</think>Answer"
print(parser.parse(early_response)) # "Answer"
# Later in training: may skip think tags for simple questions
later_response = "Answer"
print(parser.parse(later_response)) # "Answer"
See Also