Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ next_prompt_ids = r.bridge_to_next_turn(
)
```

Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `glm-5`, `glm-5.1`, `glm-4.5`, `minimax-m2`, `deepseek-v3`, `deepseek-r1`, `kimi-k2`, `kimi-k2.5` / `kimi-k2.6`, `nemotron-3`, `nemotron-3-ultra`, `llama-3`, `gpt-oss`, `hy3`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper.
Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `glm-5`, `glm-5.1`, `glm-4.5`, `minimax-m2`, `deepseek-v3`, `deepseek-r1`, `kimi-k2`, `kimi-k2.5` / `kimi-k2.6`, `nemotron-3`, `nemotron-3-ultra`, `llama-3`, `gpt-oss`, `hy3`, `inkling`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper. `qwen3-vl`, `qwen3.5`, `qwen3.6`, `kimi-k2.5` / `kimi-k2.6`, and `inkling` are multimodal (`inkling` handles both image **and** audio).

## API

Expand Down
3 changes: 2 additions & 1 deletion docs/renderer-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ chat-template kwargs. Those fields are covered by parity tests against
| Hy3 | `Hy3RendererConfig` | `reasoning_effort`, `preserved_thinking`, `is_training`, `raw_last_assistant`, `fallback_strategy` | - |
| Kimi K2 | `KimiK2RendererConfig` | - | `enable_thinking` |
| Kimi K2.5 / 2.6 | `KimiK25RendererConfig` | `thinking` | `image_cache_max` |
| Inkling | `InklingRendererConfig` | `reasoning_effort` | `image_cache_max` |
| Laguna XS.2 | `LagunaXS2RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
| Laguna XS-2.1 | `LagunaXS21RendererConfig` | `enable_thinking` | - |
| Llama 3 | `Llama3RendererConfig` | `date_string`, `tools_in_user_message` | - |
Expand Down Expand Up @@ -136,7 +137,7 @@ the knobs its template actually exposes:
| Nemotron-3 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
| DeepSeek R1 | `template` |
| MiniMax M2 | `tool_cycle` |
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / XS-2.1, Llama 3 | `all` |
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / XS-2.1, Llama 3, Inkling | `all` |
| PrimeIntellect Qwen3 | `all` |

Config construction raises when an explicit template knob directly contradicts
Expand Down
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ dependencies = [
"openai>=1.108.1",
"tiktoken",
"jinja2",
"transformers>=4.50.0",
# >=5.14.0: first release with native Inkling processor support.
"transformers>=5.14.0",
# Used by GptOssRenderer to render and parse harmony tokens. Vendoring
# OpenAI's reference implementation keeps us byte-identical with vLLM
# (which also uses it) and saves us mirroring a 330-line Jinja template.
Expand Down Expand Up @@ -101,6 +102,9 @@ exclude-newer = "7 days"
# fast-track so first-party releases can land same-day. Only packages that
# appear in `uv tree` are listed.
prime-pydantic-config = false
# transformers >=5.14 (native Inkling processor) lands inside the cooldown;
# fast-track it, and drop this once 5.14.x clears the 7-day window.
transformers = false

[tool.ty.environment]
python-version = "3.13"
Expand Down
4 changes: 4 additions & 0 deletions renderers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
GLM5RendererConfig,
GptOssRendererConfig,
Hy3RendererConfig,
InklingRendererConfig,
KimiK25RendererConfig,
KimiK2RendererConfig,
LagunaXS2RendererConfig,
Expand Down Expand Up @@ -88,6 +89,7 @@
"GLM5Renderer": "renderers.glm5",
"GptOssRenderer": "renderers.gpt_oss",
"Hy3Renderer": "renderers.hy3",
"InklingRenderer": "renderers.inkling",
"KimiK25Renderer": "renderers.kimi_k25",
"KimiK2Renderer": "renderers.kimi_k2",
"LagunaXS21Renderer": "renderers.laguna_xs2",
Expand Down Expand Up @@ -141,6 +143,8 @@ def __dir__() -> list[str]:
"Hy3Renderer",
"Hy3RendererConfig",
"ImagePart",
"InklingRenderer",
"InklingRendererConfig",
"KimiK25Renderer",
"KimiK25RendererConfig",
"KimiK2Renderer",
Expand Down
5 changes: 5 additions & 0 deletions renderers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,8 @@ def bridge_to_next_turn(self, *args: Any, **kwargs: Any) -> "RenderedTokens | No
# GPT-OSS.
"openai/gpt-oss-20b": "gpt-oss",
"openai/gpt-oss-120b": "gpt-oss",
# Thinking Machines Inkling (vision + audio; native processor in transformers >= 5.14).
"thinkingmachines/Inkling": "inkling",
# Tencent Hunyuan Hy3 (295B-A21B MoE). The FP8 checkpoint shares the same
# tokenizer and chat template. Hy3-preview is deliberately unmapped: it
# ships an older, incompatible template (un-suffixed special tokens,
Expand Down Expand Up @@ -1115,6 +1117,7 @@ def bridge_to_next_turn(self, *args: Any, **kwargs: Any) -> "RenderedTokens | No
# ``grid_thws``.
"moonshotai/Kimi-K2.5": {"image"},
"moonshotai/Kimi-K2.6": {"image"},
"thinkingmachines/Inkling": {"image", "audio"},
}


Expand Down Expand Up @@ -1313,6 +1316,7 @@ def _populate_registry():
from renderers.glm45 import GLM45Renderer
from renderers.gpt_oss import GptOssRenderer
from renderers.hy3 import Hy3Renderer
from renderers.inkling import InklingRenderer
from renderers.kimi_k2 import KimiK2Renderer
from renderers.kimi_k25 import KimiK25Renderer
from renderers.laguna_xs2 import LagunaXS2Renderer, LagunaXS21Renderer
Expand Down Expand Up @@ -1340,6 +1344,7 @@ def _populate_registry():
"deepseek-v3": DeepSeekV3Renderer,
"deepseek-r1": DeepSeekR1Renderer,
"hy3": Hy3Renderer,
"inkling": InklingRenderer,
"kimi-k2": KimiK2Renderer,
"kimi-k2.5": KimiK25Renderer,
"laguna-xs.2": LagunaXS2Renderer,
Expand Down
62 changes: 62 additions & 0 deletions renderers/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,64 @@ def _check_thinking_retention(self):
return self


# Inkling ``reasoning_effort`` label → float (the template's own effort_map).
INKLING_EFFORT_MAP: dict[str, float] = {
"none": 0.0,
"minimal": 0.1,
"low": 0.2,
"medium": 0.7,
"high": 0.9,
"max": 0.99,
}


class InklingRendererConfig(BaseRendererConfig):
"""Inkling renderer config (``thinkingmachines/Inkling``).

Inkling gates reasoning depth via a ``reasoning_effort`` knob rather
than a boolean ``enable_thinking``. It accepts either a string label
from :data:`INKLING_EFFORT_MAP` (``none`` … ``max``) or a raw float in
``[0.0, 0.99]``; the renderer emits ``Thinking effort level: {N}`` in a
leading system message exactly as the chat template does (label mapped
to its float, ``0.0`` printed as ``"0"``). The template default —
applied when no ``reasoning_effort`` is passed — is ``0.9``, which this
config mirrors.

Reasoning is preserved on every historical assistant turn (the template
has no history-dropping knob), so the effective bridge policy is
``"all"``.
"""

name: Literal["inkling"] = "inkling"

reasoning_effort: str | float = 0.9
"""Reasoning-effort gate. Mirrors the chat template's ``reasoning_effort``
kwarg: a label in :data:`INKLING_EFFORT_MAP` or a float in ``[0.0, 0.99]``.
Default ``0.9`` matches the template's own default (equivalent to
``"high"``)."""

image_cache_max: int = 256
"""FIFO bound on the per-renderer image-processor cache. Renderer-
internal — not a Jinja chat-template kwarg."""

_internal_fields = frozenset({"image_cache_max"})

@model_validator(mode="after")
def _check_reasoning_effort(self):
eff = self.reasoning_effort
if isinstance(eff, str):
if eff.strip() not in INKLING_EFFORT_MAP:
raise ValueError(
f"reasoning_effort={eff!r} is not a known label. "
f"Use one of {sorted(INKLING_EFFORT_MAP)} or a float in [0.0, 0.99]."
)
else:
num = float(eff)
if num < 0.0 or num > 0.99:
raise ValueError(f"reasoning_effort={eff!r} must be in [0.0, 0.99].")
return self


class GptOssRendererConfig(BaseRendererConfig):
"""OpenAI gpt-oss (harmony) renderer config.

Expand Down Expand Up @@ -664,6 +722,7 @@ class DeepSeekR1RendererConfig(BaseRendererConfig):
GLM45RendererConfig,
GptOssRendererConfig,
Hy3RendererConfig,
InklingRendererConfig,
KimiK2RendererConfig,
KimiK25RendererConfig,
LagunaXS2RendererConfig,
Expand Down Expand Up @@ -704,6 +763,7 @@ class DeepSeekR1RendererConfig(BaseRendererConfig):
"glm-4.5": GLM45RendererConfig,
"gpt-oss": GptOssRendererConfig,
"hy3": Hy3RendererConfig,
"inkling": InklingRendererConfig,
"kimi-k2": KimiK2RendererConfig,
"kimi-k2.5": KimiK25RendererConfig,
"laguna-xs.2": LagunaXS2RendererConfig,
Expand Down Expand Up @@ -752,6 +812,8 @@ def config_from_name(name: str) -> BaseRendererConfig | None:
"GLM5RendererConfig",
"GptOssRendererConfig",
"Hy3RendererConfig",
"INKLING_EFFORT_MAP",
"InklingRendererConfig",
"KimiK25RendererConfig",
"KimiK2RendererConfig",
"LagunaXS2RendererConfig",
Expand Down
Loading
Loading