Skip to content
Open
Show file tree
Hide file tree
Changes from all 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