nazdridoy commited on
Commit
bde6cbf
·
verified ·
1 Parent(s): 074f3bf

feat(core): enhance reasoning toggle for streaming output

Browse files

- [refactor] Simplify initial text type check (utils.py:render_with_reasoning_toggle():310)
- [feat] Implement streaming logic for reasoning removal (utils.py:313-321)
- [feat] Implement streaming logic for reasoning HTML conversion (utils.py:323-331)
- [docs] Update docstring to detail new reasoning toggle behavior (utils.py:302-309)

Files changed (1) hide show
  1. utils.py +34 -27
utils.py CHANGED
@@ -300,33 +300,40 @@ def format_access_denied_message(message: str) -> str:
300
  def render_with_reasoning_toggle(text: str, show_reasoning: bool) -> str:
301
  """Render assistant text while optionally revealing content inside <think>...</think>.
302
 
303
- When show_reasoning is True, wrap the reasoning content in a collapsible HTML details block
304
- with a fenced code block for readability. When False, strip the reasoning content entirely.
305
-
306
- This function is designed to be called repeatedly during streaming; it will simply do nothing
307
- until both opening and closing tags appear in the text.
 
 
 
 
 
308
  """
309
- if not isinstance(text, str) or "<think>" not in text:
310
  return text
311
 
312
- pattern = re.compile(r"<think>([\s\S]*?)</think>", re.IGNORECASE)
313
-
314
- # If the closing tag hasn't arrived yet (streaming), hide the partial reasoning
315
- if "</think>" not in text:
316
- # Trim everything from the first <think> onwards
317
- head = text.split("<think>", 1)[0]
318
- return head
319
-
320
- def _replace(match: re.Match) -> str:
321
- content = match.group(1).strip()
322
- if not show_reasoning:
323
- return ""
324
- # Use HTML <details> which is generally supported by Markdown renderers
325
- # and keep the reasoning in a code fence for safe rendering.
326
- return (
327
- "<details><summary>Reasoning</summary>\n\n"
328
- "```text\n" + content + "\n```\n"
329
- "</details>\n"
330
- )
331
-
332
- return pattern.sub(_replace, text)
 
 
 
300
  def render_with_reasoning_toggle(text: str, show_reasoning: bool) -> str:
301
  """Render assistant text while optionally revealing content inside <think>...</think>.
302
 
303
+ Behavior:
304
+ - When show_reasoning is True:
305
+ * Replace the opening <think> tag with a collapsible HTML <details> block and an opening
306
+ fenced code block. Stream reasoning tokens inside this block as they arrive.
307
+ * Replace the closing </think> tag with the closing fence and </details> when it appears.
308
+ - When show_reasoning is False:
309
+ * Remove complete <think>...</think> blocks.
310
+ * For partial streams (no closing tag yet), trim everything from the first <think> onward.
311
+
312
+ Safe to call on every streamed chunk; conversions are idempotent.
313
  """
314
+ if not isinstance(text, str):
315
  return text
316
 
317
+ # If we are NOT showing reasoning, remove it entirely. For partial streams, hide from <think> onwards.
318
+ if not show_reasoning:
319
+ if "<think>" not in text:
320
+ return text
321
+ if "</think>" not in text:
322
+ return text.split("<think>", 1)[0]
323
+ # Remove complete <think>...</think> blocks
324
+ pattern_strip = re.compile(r"<think>[\s\S]*?</think>", re.IGNORECASE)
325
+ return pattern_strip.sub("", text)
326
+
327
+ # Show reasoning: stream it as it arrives by converting tags into a collapsible details block
328
+ open_block = "<details><summary>Reasoning</summary>\n\n```text\n"
329
+ close_block = "\n```\n</details>\n"
330
+
331
+ # Convert opening tag when first seen; idempotent if it's already converted
332
+ if "<think>" in text:
333
+ text = re.sub(r"<think>", open_block, text, flags=re.IGNORECASE)
334
+
335
+ # Convert closing tag when it appears
336
+ if "</think>" in text:
337
+ text = re.sub(r"</think>", close_block, text, flags=re.IGNORECASE)
338
+
339
+ return text