# TML Renderers [PyPI ↗](https://pypi.org/project/tml-renderers/) ## [v0.4.1](#v0-4-1) Published November 6, 2026 ### New - Adds `renderers.KimiK31Pro()` and `tokenizers.KimiK3Tokenizer()` for Kimi K3.1-Pro. The default reasoning effort is `max`; use `.high()`, `.low()`, `.disable_thinking()`, or `.no_reasoning_effort()` for other supported modes. ### Changes - None. ### Fixed - **Kimi:** Keeps `KimiK2` and `KimiK25` streamed answer indices consistent with the completed message after an empty thinking block. - **GLM:** Trains the correct assistant-turn ending in `Glm52`, `Glm53`, and `Glm53Flash` supervised examples: `<|observation|>` after tool calls and `<|user|>` otherwise, matching the next prompt. This changes the last trained token of GLM SFT examples. - **Qwen:** Corrects `Qwen35Tokenizer` token IDs for more text. This changes rendered tokens in Qwen3.5 and Qwen3.8 prompts and SFT examples. ### Deprecations - None. ### Breaking changes - None. ### Dependencies - Python `>=3.10` → `>=3.11` ## [v0.4.0](#v0-4-0) Published November 5, 2026 ### New - Use [`renderers.Inkling()`](https://tinker-docs.thinkingmachines.ai/cookbook/inkling/tml-renderers/) for Inkling and Inkling-Small, or `renderers.Inkling2()` for Inkling 2 Small. Each builds its matching tokenizer. Set thinking effort with `render_for_completion_with_effort(messages, effort)`. - Use `tokenizers.H200kBaseChatTokenizer()`, the Inkling 2 Small tokenizer, with low-level TMLv0 rendering: `v0.Renderer(tokenizers.H200kBaseChatTokenizer())`. ### Changes - None. ### Fixed - **GLM:** Corrects `GlmTokenizer` token IDs for some text. This changes rendered tokens for `Glm52`, `Glm53`, and `Glm53Flash`. - **Qwen:** Corrects `Qwen35Tokenizer` token IDs for some text. This changes rendered tokens for `Qwen35` and `Qwen38`. ### Deprecations - None. ### Breaking changes - [`parser.parse_tokens()` and `parser.parse()`](https://tinker-docs.thinkingmachines.ai/cookbook/inkling/tml-renderers/#parser) now return `ParseResult` instead of a list. For list-specific operations, replace `messages = parser.parse_tokens(tokens)` with `messages = parser.parse_tokens(tokens).messages`; iteration and integer indexing still work on the result. `result.token_counts` reports counts per completed message, and streaming completion updates expose `num_tokens`. - `renderers.DeepSeekR1` and `tokenizers.DeepSeekR1Tokenizer` are removed. Select a supported model and its matching renderer/tokenizer before upgrading; applications that still require these R1 APIs must remain on 0.3.0. - A `v0.Renderer` pickled with 0.3.0 or earlier raises `TypeError` when loaded with 0.4.0. Recreate and re-save renderers after upgrading, and use matching package versions in workers that exchange them. ### Dependencies - Drops support for Python `3.10`. ## [v0.3.0](#v0-3-0) Published November 3, 2026 ### New - Parse sampled tokens incrementally with [`parser.parse_stream()`](https://tinker-docs.thinkingmachines.ai/cookbook/inkling/tml-renderers/#parser). Inside its context manager, call `stream.feed(token_ids)` for each chunk and `stream.finish()` at completion; each returns `ParseUpdate` objects. - Continue a conversation without re-encoding its sampled prefix using `renderer.bridge_to_next_turn(prefix, message_history, next_messages)`. It returns the combined token spans, the appended suffix, and a parser for the next turn. - Use `renderers.Nemotron3VL()` and `tokenizers.Nemotron3VLTokenizer()` for Nemotron 3.5 Super VL, including image inputs. Call `.disable_thinking()` for the non-thinking preset. - Preserve DeepSeek-V4.1 reasoning across turns with `renderers.DeepSeekV41.preserve_thinking_history()` or `DeepSeekV41(strip_thinking_from_history=False)`. - Use `chat.ParseErrorContent(error_message=..., sampled_tokens=...)` to record a failed parse in a conversation. Renderers write only its `error_message` into the prompt. DeepSeek-V3.1 does not accept it, and DeepSeek-V4, DeepSeek-V4.1, GLM, and GPT-OSS accept it only in user messages. - Convert OpenAI Chat Completions messages with [`MessageList.from_openai_chat_dicts(messages, tools=...)`](https://tinker-docs.thinkingmachines.ai/cookbook/inkling/tml-renderers/#message-conversion) and convert back with `to_openai_chat_dicts()`. A top-level `reasoning_content` becomes reasoning, and the request's `tools` list becomes a tool declaration. `OpenAIMessage` has the same methods. ### Changes - None. ### Fixed - Accepts repeated argument names when constructing or copying `InvokeTool` calls, such as calls returned by the parser. 0.2.x raised `ValueError`. - Keeps assistant dictionaries with `content: []` as empty assistant turns during conversion, instead of dropping them. - Rejects zero-width or zero-height image pointers during image-dictionary conversion instead of failing later during rendering. - Encodes adjacent same-weight text together in Qwen and Nemotron supervised targets, so the tokens match what the model samples. This changes SFT tokens. - Corrects whitespace, newlines, and tool-result order in Qwen, Nemotron, GLM, and DeepSeek-V4 prompts. This changes prompt tokens for some conversations. - Smaller parsing fixes for GLM, Nemotron, DeepSeek-V4, GPT-OSS, and Kimi: unfinished tool calls, string arguments, message authors, and adjacent assistant turns. ### Deprecations - The `from_oss_*` and `to_oss_*` methods of `OpenAIMessage` and `MessageList` are deprecated and emit `DeprecationWarning`. Use the `*_openai_chat_*` methods, or `tinker_cookbook.third_party.tml_renderers_compat` for cookbook-format messages. No removal version or date is announced. ### Breaking changes - The TMLv0, Qwen3, Qwen3.5, Qwen3.8, Nemotron 3, DeepSeek-R1, and DeepSeek-V3.1 parsers now raise when a completion stops inside a message, instead of returning only the completed messages. The error is a `ValueError` (`v0.ParseError` for TMLv0). Catch `ValueError` and read `error.completed_messages` to recover the completed messages. DeepSeek-V3, V4, and V4.1 errors now carry `completed_messages` too. This applies to both [`parse_tokens()` and `parse()`](https://tinker-docs.thinkingmachines.ai/cookbook/inkling/tml-renderers/#parser). - `Glm53` and `Glm53Flash` now preserve historical reasoning by default (`clear_thinking=False`), changing prompts and supervised examples. Pass `clear_thinking=True`, including to `.high()` or `.low()`, to retain the previous behavior consistently in sampling and training. - The `from_oss_dict()` and `from_oss_messages()` readers now reject a top-level `reasoning_content` instead of dropping it. Read OpenAI Chat dicts with the [`from_openai_chat_dict` or `from_openai_chat_dicts` helpers](https://tinker-docs.thinkingmachines.ai/cookbook/inkling/tml-renderers/#message-conversion). ### Dependencies - None. ## [v0.2.1](#v0-2-1) Published October 22, 2026 ### New - None. ### Changes - `renderers.GptOss()` now defaults to `reasoning_effort="medium"` instead of `"low"`. Pass `reasoning_effort="low"` to retain the previous prompt setting. ### Fixed - Allows renderers and parsers to be used from a Python thread other than the one that created them, without raising `PanicException`. - Prevents a deadlock when multiple threads construct their first tokenizer concurrently. - When a reply is cut off after one or more finished messages, `parse_tokens()` and `parse()` return the finished messages instead of raising. This applies to TMLv0, Qwen3, Qwen3.5, Qwen3.8, Nemotron 3, DeepSeek-R1, and DeepSeek-V3.1 in thinking mode. A reply cut off in its first message still raises. - Places system text at a fixed position in Qwen and Nemotron prompts that declare tools, whatever the message order: before the tool declarations for Qwen3 and Nemotron 3, and after them for Qwen3.5 and Qwen3.8, with a blank line before it. This changes prompt tokens, for example for a Qwen3.5 system message given before the tool declaration. - Uses the correct special-token IDs in Qwen3.5 and Nemotron tool-declaration examples. User-provided text and schema strings remain literal text. - **TMLv0:** Parses an empty message as `Text("")` and keeps every value of a repeated tool-call argument, instead of raising `v0.ParseError`. ### Deprecations - None. ### Breaking changes - **TMLv0:** Writes the tool name in the header of each structured tool-call message, for example `<|message_model|>get_weather<|content_invoke_tool_json|>`. This restores the 0.1.0 tokens for tool calls converted from OpenAI-style messages, and adds the name for tool calls without an author name. A tool call whose `author.name` differs from the tool name now raises `ValueError`; unset `author.name` or set it to the tool name. ### Dependencies - None. ## [v0.2.0](#v0-2-0) Published October 14, 2026 ### New - Introducing support for other model families in `tml-renderers`: Qwen3, Qwen3.5, Qwen3.8, DeepSeek-V3, DeepSeek-R1, DeepSeek-V3.1, DeepSeek-V4, DeepSeek-V4.1, Kimi K2, Kimi K2.5, GLM-5.2, GLM-5.3, GLM-5.3-Flash, GPT-OSS, and Nemotron 3. - Set `chat.ImagePointer.override_width` and `override_height` to request a rendering resolution different from the stored image dimensions. Rendered image spans carry the resolved `virtual_width` and `virtual_height`. - Normalize native or OpenAI-style message sequences with `chat.MessageList.from_messages()`. The helper also accepts an existing `MessageList`. - Pass a tool failure as a content part such as `{"type": "tool_error", "text": "command failed"}` in OpenAI-style messages. It converts to `chat.ToolError`, and TMLv0 renders it as a tool error. - Pickle renderers, for example to send them to worker processes. This includes `v0.Renderer`. - Use `renderer.tokenizer` to get a renderer's tokenizer, and type code that accepts any renderer with `tml_renderers.Renderer`. ### Changes - **TMLv0:** Parsed tool calls now carry a `tool_call_id` numbered in call order, for example `functions.read:0`. 0.1.0 returned `None`. ### Fixed - **TMLv0:** Rejects TML image pointers with non-positive dimensions instead of silently rendering an image with zero tokens. - Preserves WAV audio through OSS message round trips. - **TMLv0:** Rejects sampled tool calls with an unknown field or an empty tool name with `v0.ParseError`. 0.1.0 dropped the field, for example `"trace"` in `{"name": "read", "args": {}, "trace": "abc"}`, and accepted the empty name. ### Deprecations - `tokenizers.o200k_base_chat()` is deprecated; use `tokenizers.O200kBaseChatTokenizer()`. The old factory remains available and emits `DeprecationWarning`. No removal version or date is announced. - `tml_renderers.tinker.TokenSpanWrapper` and `RawTokenSpan` remain as deprecated compatibility names. Annotate rendered spans with `chat.TokenSpan` and access the contained value through `.span`. No removal version or date is announced. ### Breaking changes - The old incremental parser methods `parse_token()`, `parse_updates()`, `flush_updates()`, `flush()`, `reset()`, and `is_at_message_boundary()` are removed. Accumulate a completion and pass it to `parse_tokens()` or `parse()`; each call handles a complete parsing attempt. - `parse_errors.UnterminatedMessageError` is replaced by `UnterminatedStructureError` and `UnterminatedContentError`. Update imports and checks of `ParseError.kind` to distinguish incomplete structure from incomplete content. - **TMLv0:** `parse_tokens()` and `parse()` now raise `v0.ParseError` when a completion stops inside a message, even when earlier messages completed. 0.1.0 returned only the completed messages, or `[]`. A completion with no message, such as a lone end-of-sampling token, also raises (`EmptyResponseError`). Catch `v0.ParseError` when a completion can reach a token limit. - `chat.InvokeTool` raises `ValueError` for an empty tool name, a repeated argument name, or raw `text` combined with a structured call, on construction and on `copy()`. 0.1.0 accepted these. Remove repeated arguments, and use `chat.InvokeTool("raw text")` for a raw-text call. ### Dependencies - Adds support for Python `3.10`. ## [v0.1.0](#v0-1-0) Published July 15, 2026 ### New - Introducing `tml-renderers`, including the TMLv0 renderer for use with Thinking Machines’ Inkling model. ### Changes - None. ### Fixed - None. ### Deprecations - None. ### Breaking changes - None. ### Dependencies - Requires Python `>=3.11`. - PyTorch `>=2.10`. Install it separately; the wheel does not install it.