feat(renderers): add Qwen3.8-27B (dense) renderer - #132
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ 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.
| "prime-qwen3": PrimeQwen3RendererConfig, | ||
| "qwen3.5": Qwen35RendererConfig, | ||
| "qwen3.6": Qwen36RendererConfig, | ||
| "qwen3.8": Qwen38RendererConfig, |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit 5520d1e. Configure here.
| else: | ||
| reasoning_content = before_think_end.lstrip("\n") | ||
| reasoning_content = reasoning_content.rstrip("\n") | ||
| content = after_think_end.lstrip("\n") |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit 5520d1e. Configure here.
ApprovabilityVerdict: 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:
You can add or adjust custom eligibility rules. Learn more. |


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):reasoning_effortinstructions — when thinking is enabled, the template resolvesreasoning_effort(xhighdefault) into areasoning_instructionsstring injected into the leading system message (tools path, no-tools path, and as a standalone system message when the caller supplies none).mediumsuppresses it.preserve_thinkingis undefined in the template and treated asTrue, so historicalthinkingblocks are kept on every assistant turn, not just after the last real user query. The config therefore defaultspreserve_thinking=True.responsefallback — the template no longer derives reasoning fromcontentwhenreasoning_contentis 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— newQwen38Renderer(subclass ofQwen35Renderer) implementing the three deltas;reasoning_effort/preserve_thinking/enable_thinkingknobs.renderers/configs.py— newQwen38RendererConfigwith_template_fieldsallowlist (enable_thinking,add_vision_id,preserve_thinking,reasoning_effort).renderers/base.py— registerqwen3.8renderer; mapQwen/Qwen3.8-27B; add toMULTIMODAL_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_toolsand reasoning extraction into_extract_reasoning, both overridable byQwen38Renderer. No behavior change for Qwen3.5 (parity tests confirm).tests/test_qwen38.py— size-coverage + polarity + byte-parity againstapply_chat_templatefor 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 (xhighadded toreasoning_effortvalues;qwen3.8added to the disabled-thinking deviation tuple).Testing
origin/mainin this environment: 12 Inkling + 4 multimodal/client tests fail identically without this change (transformers-version image-processor issue) — unrelated to Qwen3.8.reasoning_effortlow/medium/xhigh,preserve_thinkingTrue/False,enable_thinkingFalse, 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.8renderer forQwen/Qwen3.8-27Bso that model can be tokenized with full chat-template parity instead of falling through to a generic path.Qwen38RenderersubclassesQwen35Rendererand implements the template deltas: injectreasoning_effortinstructions (xhigh/low/medium) into the leading system message (including a standalone system turn when none is supplied), defaultpreserve_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 areenable_thinking,preserve_thinking,reasoning_effort, andadd_vision_id.Qwen35Rendererextracts_emit_system_and_toolsand_extract_reasoningso the subclass can override them without changing Qwen3.5 output. The model is registered inMODEL_RENDERER_MAPandMULTIMODAL_MODELS, and tests cover mapping, thinking defaults, and byte parity againstapply_chat_template.Reviewed by Cursor Bugbot for commit 5520d1e. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add
Qwen38Rendererfor Qwen3.8-27B (dense) modelQwen/Qwen3.8-27BinMODEL_RENDERER_MAPandMULTIMODAL_MODELS(image-capable) in base.py, routing it to a newQwen38Rendererinstead of the default.Qwen38RendererConfigin configs.py with fieldsenable_thinking,reasoning_effort(default xhigh),preserve_thinking(default True), andadd_vision_id.Qwen38Rendererin qwen38.py injects reasoning-effort instructions into the system message, JSON-encodes non-string tool arguments, and sources assistant reasoning solely fromreasoning_contentwith no content-based fallback.Qwen35Renderer._render_assistantin qwen35.py to use a new_extract_reasoninghelper that splits on" response"instead of ``.Qwen35Renderer._extract_reasoningnow keys on" response"delimiter rather than ``, affecting assistant message parsing for Qwen3.5 when no explicitreasoning_contentis present. `Qwen38Renderer._render_arg_value` JSON-encodes non-string tool arguments, changing serialization for booleans/numbers.Macroscope summarized 5520d1e.