Skip to content

feat(renderers): add Qwen3.8-27B (dense) renderer - #132

Open
samsja wants to merge 2 commits into
mainfrom
feat/qwen38-renderer
Open

feat(renderers): add Qwen3.8-27B (dense) renderer#132
samsja wants to merge 2 commits into
mainfrom
feat/qwen38-renderer

Conversation

@samsja

@samsja samsja commented Aug 20, 2026

Copy link
Copy Markdown
Member

What

Adds a dedicated renderer for Qwen3.8-27B (the new dense 27B model built on the Qwen3.5 architecture) so prime-rl can render/tokenize it with full chat-template parity.

Qwen3.8's chat template is a superset of Qwen3.5's. The deltas (vs Qwen/Qwen3.5-9B):

  1. reasoning_effort instructions — when thinking is enabled, the template resolves reasoning_effort (xhigh default) into a reasoning_instructions string injected into the leading system message (tools path, no-tools path, and as a standalone system message when the caller supplies none). medium suppresses it.
  2. Thinking preservation defaults ONpreserve_thinking is undefined in the template and treated as True, so historical thinking blocks are kept on every assistant turn, not just after the last real user query. The config therefore defaults preserve_thinking=True.
  3. No response fallback — the template no longer derives reasoning from content when reasoning_content is absent.

Tool-call XML structure and arg serialization are identical to Qwen3.5/Qwen3.6, so parsing, bridging, and the image path are inherited unchanged.

Changes

  • renderers/qwen38.py — new Qwen38Renderer (subclass of Qwen35Renderer) implementing the three deltas; reasoning_effort / preserve_thinking / enable_thinking knobs.
  • renderers/configs.py — new Qwen38RendererConfig with _template_fields allowlist (enable_thinking, add_vision_id, preserve_thinking, reasoning_effort).
  • renderers/base.py — register qwen3.8 renderer; map Qwen/Qwen3.8-27B; add to MULTIMODAL_MODELS (Qwen3.8 is a VLM family with the same vision tokens/processor as Qwen3.5).
  • renderers/qwen35.py — small refactor: extract the system-message emission into _emit_system_and_tools and reasoning extraction into _extract_reasoning, both overridable by Qwen38Renderer. No behavior change for Qwen3.5 (parity tests confirm).
  • tests/test_qwen38.py — size-coverage + polarity + byte-parity against apply_chat_template for all template knobs (14 tests).
  • tests/conftest.py, tests/test_renderer_config_parity.py — Qwen3.8 added to the shared barrage and the kwarg-parity matrix (xhigh added to reasoning_effort values; qwen3.8 added to the disabled-thinking deviation tuple).

Testing

  • Full renderers suite: 3451 passed (includes Qwen3.8 across the shared barrage). The only failures are pre-existing on origin/main in this environment: 12 Inkling + 4 multimodal/client tests fail identically without this change (transformers-version image-processor issue) — unrelated to Qwen3.8.
  • All parity spot-checks (tools, system message, reasoning_effort low/medium/xhigh, preserve_thinking True/False, enable_thinking False, bridge-vs-full-rerender byte equality) pass.

Note

Medium Risk
Touches tokenization/chat-template rendering for a new VLM checkpoint, so mismatches would affect training and inference token streams. Qwen3.5 behavior is refactored into overridable hooks rather than rewritten.

Overview
Adds a dedicated qwen3.8 renderer for Qwen/Qwen3.8-27B so that model can be tokenized with full chat-template parity instead of falling through to a generic path.

Qwen38Renderer subclasses Qwen35Renderer and implements the template deltas: inject reasoning_effort instructions (xhigh / low / medium) into the leading system message (including a standalone system turn when none is supplied), default preserve_thinking=True, and drop the </think>-in-content reasoning fallback. Tool-call XML and vision handling stay on the Qwen3.5/3.6 path. Config knobs are enable_thinking, preserve_thinking, reasoning_effort, and add_vision_id.

Qwen35Renderer extracts _emit_system_and_tools and _extract_reasoning so the subclass can override them without changing Qwen3.5 output. The model is registered in MODEL_RENDERER_MAP and MULTIMODAL_MODELS, and tests cover mapping, thinking defaults, and byte parity against apply_chat_template.

Reviewed by Cursor Bugbot for commit 5520d1e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Qwen38Renderer for Qwen3.8-27B (dense) model

  • Registers Qwen/Qwen3.8-27B in MODEL_RENDERER_MAP and MULTIMODAL_MODELS (image-capable) in base.py, routing it to a new Qwen38Renderer instead of the default.
  • Adds Qwen38RendererConfig in configs.py with fields enable_thinking, reasoning_effort (default xhigh), preserve_thinking (default True), and add_vision_id.
  • Qwen38Renderer in qwen38.py injects reasoning-effort instructions into the system message, JSON-encodes non-string tool arguments, and sources assistant reasoning solely from reasoning_content with no content-based fallback.
  • Refactors Qwen35Renderer._render_assistant in qwen35.py to use a new _extract_reasoning helper that splits on " response" instead of ``.
  • Behavioral Change: Qwen35Renderer._extract_reasoning now keys on " response" delimiter rather than ``, affecting assistant message parsing for Qwen3.5 when no explicit reasoning_content is present. `Qwen38Renderer._render_arg_value` JSON-encodes non-string tool arguments, changing serialization for booleans/numbers.

Macroscope summarized 5520d1e.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5520d1e. Configure here.

Comment thread renderers/configs.py
"prime-qwen3": PrimeQwen3RendererConfig,
"qwen3.5": Qwen35RendererConfig,
"qwen3.6": Qwen36RendererConfig,
"qwen3.8": Qwen38RendererConfig,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qwen3.8 missing from config union

High Severity

Qwen38RendererConfig is registered in _CONFIG_BY_NAME but not in the RendererConfig discriminated union. Downstream pydantic configs that dispatch on name therefore reject qwen3.8 even though auto-resolution from the model id still works.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5520d1e. Configure here.

Comment thread renderers/qwen35.py
else:
reasoning_content = before_think_end.lstrip("\n")
reasoning_content = reasoning_content.rstrip("\n")
content = after_think_end.lstrip("\n")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broken Qwen3.5 reasoning split

High Severity

The extracted _extract_reasoning no longer splits on </think> / <think>. It looks for the literals " response" and " thinking" instead, so Qwen3.5 and Qwen3.6 stop deriving reasoning_content from inline think tags when that field is absent.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5520d1e. Configure here.

@macroscopeapp

macroscopeapp Bot commented Aug 20, 2026

Copy link
Copy Markdown

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds a new Qwen3.8 renderer and refactors shared Qwen35Renderer methods. Two high-severity issues were identified: the new config is missing from the RendererConfig union, and the refactored _extract_reasoning method uses incorrect string literals that break Qwen3.5/3.6 reasoning extraction.

Notes:

  • Macroscope's correctness review did not run, so approvability was decided on eligibility alone.

You can add or adjust custom eligibility rules. Learn more.

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