@@ -343,6 +343,8 @@ def create_renderer_pool(
343343 * ,
344344 tool_parser : str | None = None ,
345345 reasoning_parser : str | None = None ,
346+ preserve_all_thinking : bool = False ,
347+ preserve_thinking_between_tool_calls : bool = False ,
346348) -> RendererPool :
347349 """Create a RendererPool with *size* independent tokenizer copies.
348350
@@ -352,6 +354,11 @@ def create_renderer_pool(
352354
353355 ``tool_parser`` and ``reasoning_parser`` are forwarded to
354356 ``create_renderer`` when the pool falls back to ``DefaultRenderer``.
357+
358+ ``preserve_all_thinking`` and ``preserve_thinking_between_tool_calls``
359+ are forwarded to ``create_renderer`` and bound as defaults on each
360+ pooled instance — every ``render`` / ``render_ids`` call uses them
361+ unless an explicit kwarg overrides at the call site.
355362 """
356363
357364 def factory () -> Renderer :
@@ -365,6 +372,8 @@ def factory() -> Renderer:
365372 renderer = renderer ,
366373 tool_parser = tool_parser ,
367374 reasoning_parser = reasoning_parser ,
375+ preserve_all_thinking = preserve_all_thinking ,
376+ preserve_thinking_between_tool_calls = preserve_thinking_between_tool_calls ,
368377 )
369378
370379 return RendererPool (factory , size = size )
@@ -376,6 +385,8 @@ def create_renderer(
376385 * ,
377386 tool_parser : str | None = None ,
378387 reasoning_parser : str | None = None ,
388+ preserve_all_thinking : bool = False ,
389+ preserve_thinking_between_tool_calls : bool = False ,
379390) -> Renderer :
380391 """Create a Renderer by name, or auto-detect from the tokenizer's model name.
381392
@@ -389,6 +400,15 @@ def create_renderer(
389400 have their own parsing wired in.
390401 reasoning_parser: Name of a reasoning parser registered in
391402 ``renderers.parsers``. Only consumed by DefaultRenderer.
403+ preserve_all_thinking: Bind as default for every ``render`` /
404+ ``render_ids`` call on the returned instance. Same
405+ semantics as the call-site kwarg of the same name —
406+ see ``Renderer.render`` and
407+ ``should_preserve_past_thinking``. Off by default
408+ (zero behaviour change).
409+ preserve_thinking_between_tool_calls: Bind as default for every
410+ ``render`` / ``render_ids`` call on the returned
411+ instance. See ``Renderer.render`` for semantics.
392412 """
393413 _populate_registry ()
394414
@@ -405,15 +425,21 @@ def create_renderer(
405425 f"Unknown renderer { renderer !r} . Available: { ', ' .join (sorted (RENDERER_REGISTRY ))} "
406426 )
407427 if renderer == "default" :
408- return cls (tokenizer , ** default_kwargs )
409- if default_kwargs :
410- logger .info (
411- "tool_parser / reasoning_parser are only consumed by "
412- "DefaultRenderer; ignoring for renderer=%r which has "
413- "built-in behavior." ,
414- renderer ,
415- )
416- return cls (tokenizer )
428+ instance = cls (tokenizer , ** default_kwargs )
429+ else :
430+ if default_kwargs :
431+ logger .info (
432+ "tool_parser / reasoning_parser are only consumed by "
433+ "DefaultRenderer; ignoring for renderer=%r which has "
434+ "built-in behavior." ,
435+ renderer ,
436+ )
437+ instance = cls (tokenizer )
438+ return _bind_preserve_defaults (
439+ instance ,
440+ preserve_all_thinking = preserve_all_thinking ,
441+ preserve_thinking_between_tool_calls = preserve_thinking_between_tool_calls ,
442+ )
417443
418444 # Auto-detect from model name via exact match on the canonical HF id.
419445 # Fine-tunes and renamed checkpoints miss on purpose — their chat
@@ -423,7 +449,11 @@ def create_renderer(
423449 model_name = getattr (tokenizer , "name_or_path" , "" )
424450 renderer_name = MODEL_RENDERER_MAP .get (model_name )
425451 if renderer_name is not None :
426- return RENDERER_REGISTRY [renderer_name ](tokenizer )
452+ return _bind_preserve_defaults (
453+ RENDERER_REGISTRY [renderer_name ](tokenizer ),
454+ preserve_all_thinking = preserve_all_thinking ,
455+ preserve_thinking_between_tool_calls = preserve_thinking_between_tool_calls ,
456+ )
427457
428458 # No match — fall back to default (apply_chat_template). For fine-tunes
429459 # with customized chat templates this is the *correct* choice, so we don't
@@ -434,7 +464,76 @@ def create_renderer(
434464 "reasoning_parser=<name> to enable structured output parsing." ,
435465 model_name or "<unnamed tokenizer>" ,
436466 )
437- return RENDERER_REGISTRY ["default" ](tokenizer , ** default_kwargs )
467+ return _bind_preserve_defaults (
468+ RENDERER_REGISTRY ["default" ](tokenizer , ** default_kwargs ),
469+ preserve_all_thinking = preserve_all_thinking ,
470+ preserve_thinking_between_tool_calls = preserve_thinking_between_tool_calls ,
471+ )
472+
473+
474+ def _bind_preserve_defaults (
475+ renderer : Renderer ,
476+ * ,
477+ preserve_all_thinking : bool ,
478+ preserve_thinking_between_tool_calls : bool ,
479+ ) -> Renderer :
480+ """Bind ``preserve_*_thinking`` defaults onto a renderer instance.
481+
482+ When neither flag is set, returns ``renderer`` unchanged — zero-cost
483+ no-op for the common path. Otherwise wraps ``render`` and
484+ ``render_ids`` so every call defaults the kwargs to the bound values
485+ while still letting an explicit call-site kwarg of ``True`` override
486+ upward (the flags only ever add thinking, never strip it).
487+
488+ The two flags also become introspectable on the instance as
489+ ``_preserve_all_thinking`` / ``_preserve_thinking_between_tool_calls``
490+ so tests and downstream code can confirm what was bound.
491+ """
492+ renderer ._preserve_all_thinking = preserve_all_thinking # type: ignore[attr-defined]
493+ renderer ._preserve_thinking_between_tool_calls = ( # type: ignore[attr-defined]
494+ preserve_thinking_between_tool_calls
495+ )
496+ if not (preserve_all_thinking or preserve_thinking_between_tool_calls ):
497+ return renderer
498+
499+ orig_render = renderer .render
500+ orig_render_ids = renderer .render_ids
501+
502+ def render (
503+ messages : list [Message ],
504+ * ,
505+ tools : list [ToolSpec ] | None = None ,
506+ add_generation_prompt : bool = False ,
507+ preserve_all_thinking : bool = preserve_all_thinking ,
508+ preserve_thinking_between_tool_calls : bool = preserve_thinking_between_tool_calls ,
509+ ) -> RenderedTokens :
510+ return orig_render (
511+ messages ,
512+ tools = tools ,
513+ add_generation_prompt = add_generation_prompt ,
514+ preserve_all_thinking = preserve_all_thinking ,
515+ preserve_thinking_between_tool_calls = preserve_thinking_between_tool_calls ,
516+ )
517+
518+ def render_ids (
519+ messages : list [Message ],
520+ * ,
521+ tools : list [ToolSpec ] | None = None ,
522+ add_generation_prompt : bool = False ,
523+ preserve_all_thinking : bool = preserve_all_thinking ,
524+ preserve_thinking_between_tool_calls : bool = preserve_thinking_between_tool_calls ,
525+ ) -> list [int ]:
526+ return orig_render_ids (
527+ messages ,
528+ tools = tools ,
529+ add_generation_prompt = add_generation_prompt ,
530+ preserve_all_thinking = preserve_all_thinking ,
531+ preserve_thinking_between_tool_calls = preserve_thinking_between_tool_calls ,
532+ )
533+
534+ renderer .render = render # type: ignore[method-assign]
535+ renderer .render_ids = render_ids # type: ignore[method-assign]
536+ return renderer
438537
439538
440539# ---------------------------------------------------------------------------
0 commit comments