Skip to content

[rl] Pin renderers 0.1.11, render with TorchTitan's tokenizer, take the renderer's typed config directly - #43

Open
felipemello1 wants to merge 1 commit into
mainfrom
80-renderers-typed
Open

[rl] Pin renderers 0.1.11, render with TorchTitan's tokenizer, take the renderer's typed config directly#43
felipemello1 wants to merge 1 commit into
mainfrom
80-renderers-typed

Conversation

@felipemello1

@felipemello1 felipemello1 commented Sep 2, 2026

Copy link
Copy Markdown
Owner

TLDR:

  • pin renderers
  • Use our tokenizer and remove transformers dependency
  • Simplify code by using config classes directly rather than trying to map model_name -> config_type
  • Simplify code by removing the need of registering muse glimmer

Summary

TitanRL uses renderers to turn chat messages into token ids and parse completions back. Until now we wrapped it in our own RendererConfig:

# before: recipe
renderer=RendererConfig(name="qwen3", enable_thinking=False)

# before: RendererConfig.build()
tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)                  # transformers
args = {f.name: v for f in fields(self) if f.name in config_type.model_fields}   # silent drop
return create_renderer(tokenizer, config_type(**args))

Problems:

  1. Silent drops ([rl] RendererConfig silently drops the removed preserve_* renderer knobs pytorch/torchtitan#4365). build() forwarded knobs by name-matching against the renderer's config and dropped the rest. When renderers replaced preserve_all_thinking with thinking_retention, the knob went inert with no error; two in-tree recipes were already hitting the same thing (enable_thinking on gpt-oss and Muse Glimmer, which have no such option).
  2. A second tokenizer. build() loaded transformers.AutoTokenizer even though TorchTitan already has the tokenizer, and the controller then reached into renderer._tokenizer for pad_id.
  3. Transformers dependency

Solutions:

renderers==0.1.11 accepts a bring-your-own tokenizer and drops the transformers dependency:

  1. Recipes hold the library's typed config directly: We don't have a wrapper anymore. We don't try to redirect 'qwen3to the right config, e.g.renderer=RendererConfig(name="qwen3", enable_thinking=False)`. Instead, we do:

    from renderers import Qwen3RendererConfig, GptOssRendererConfig
    renderer=Qwen3RendererConfig(enable_thinking=False)
    renderer=GptOssRendererConfig(reasoning_effort="low")
  2. We load our own tokenizer:build_renderer pairs that config with TorchTitan's tokenizer, through a small adapter (RendererTokenizer) that exposes the interface renderers expects.

    # controller / rollout worker / generate.py
    tokenizer = HuggingFaceTokenizer(tokenizer_path=hf_assets_path)
    renderer = build_renderer(tokenizer=tokenizer, config=config.renderer)
    pad_id = tokenizer.eos_id
    
    def build_renderer(*, tokenizer: HuggingFaceTokenizer, config: BaseRendererConfig) -> Renderer:
        if config.name == "auto": raise ValueError(...)      # unmatched local paths fall back unsafely
        if config.name == "default": raise ValueError(...)   # needs HF apply_chat_template
        renderer_tokenizer = RendererTokenizer(tokenizer)
        if isinstance(config, TorchTitanRendererConfig):     # renderer implemented in TorchTitan
            return config.renderer_cls(renderer_tokenizer, config)
        return create_renderer(tokenizer=renderer_tokenizer, config=config)
  3. We skip registration of new renderers*: Muse Glimmer's renderer lives in TorchTitan, so the renderer's registry cannot find it. We skip the need for the renderers registry that maps config -> renderer class. Check TorchTitanRendererConfig.

Validation

  • Alphabet sort, Qwen3-0.6B, 2 GPUs: validation reward 0.181 -> 0.548.

@felipemello1
felipemello1 force-pushed the 80-renderers-typed branch 9 times, most recently from 65d12cd to 0d1de2a Compare September 3, 2026 15:11
@felipemello1 felipemello1 changed the title [rl] Pin renderers 0.1.11, BYO tokenizer, take the renderer's typed config directly (alternative to #42) [rl] Pin renderers 0.1.11, render with TorchTitan's tokenizer, take the renderer's typed config directly Sep 3, 2026
…he renderer's typed config directly

renderers 0.1.11 makes transformers optional and accepts a bring-your-own
tokenizer. RL now renders with TorchTitan's HuggingFaceTokenizer (wrapped in
RendererTokenizer to satisfy renderers.OffsetTokenizer) instead of loading
transformers.AutoTokenizer, and the controller reads pad_id off its own
tokenizer instead of renderer._tokenizer.

TorchTitan's RendererConfig wrapper is removed. Controller.Config.renderer is
the library's own typed pydantic config (Qwen3RendererConfig(enable_thinking=False),
GptOssRendererConfig(reasoning_effort="low"), ...), so a wrong option fails
when the recipe is constructed instead of being silently dropped (pytorch#4365), and
TorchTitan mirrors no renderer fields. build_renderer(tokenizer, config) is the
one TorchTitan-side seam. A renderer that ships in TorchTitan (Muse Glimmer)
has a TorchTitanRendererConfig that names its renderer class, and build_renderer
constructs it directly, so nothing is written into renderers' registry.
AutoRendererConfig and DefaultRendererConfig are rejected with the reason: the
former depends on an exact model-ID match that local asset paths do not reliably
preserve; the latter needs Hugging Face-compatible apply_chat_template semantics,
which TorchTitan's template rendering does not provide.
Renderer options are set in the recipe (tyro.conf.Suppress, like model_spec);
the 8 redundant --renderer.enable-thinking CLI flags in the integration tests
are removed. Configurable.to_dict learns pydantic model_dump so the wandb config
stays JSON.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant