Repository navigation
Add TeleChat - #50477
Add TeleChat#50477hmellor wants to merge 17 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. Pages changed in this PR: |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The checkpoints' tokenizer_config.json names tokenizer classes that only exist in their remote code. They are thin SentencePiece wrappers, so route these model types to TokenizersBackend and make the generic conversion from a bare tokenizer.model follow the SentencePiece model's own settings: - extra whitespace removal, dummy prefix and whitespace escaping from the normalizer spec - BPE merges ranked by piece score - user-defined pieces emitted whole and never merged - added tokens matched on the raw text when there is a dummy prefix - added_tokens_decoder may swap two pieces' ids Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Their tokenizers now load natively too (huggingface/transformers#50477), so the test entries no longer set trust_remote_code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
…inst the tokenized text The dummy-prefix `Strip` decoder applies per token, so without byte fallback (and its `Fuse`) it stripped the space from every word. The replacement offsets test decoded the input ids to recover the expanded text, which only works for tokenizers that round-trip their input; SentencePiece-based ones don't around added tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers modeling backend These need huggingface/transformers#50477. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
vasqu
left a comment
There was a problem hiding this comment.
Hmm I'm not 100% sure here because a lot we probably could cover by using conversion mappings + updating the remote configs. The question is: Are they open to change their model type on the hub?
My gripe essentially is that we add 4 times llama but just slightly different configs to describe the same things. If we get other options to do it, I'd really love to ngl. Even frankenmerged configs (so the original entries + the entries we would use on transformers side living side by side)
This doesnt mean I'm opposed but we need to be careful to not add the same model again and again
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
They only differ from Llama in their configs, so keep only the configs and point the auto mappings at the Llama classes. InternLM3's `qkv_bias` and `bias` are loaded as `attention_bias` and `mlp_bias`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The original checkpoints fuse keys and values per head (`[k_0, v_0, k_1, v_1, ...]`). Split them into `k_proj` and `v_proj` on load, with `Interleave` now taking a block size, so TeleChat uses Llama's attention forward. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Their configs are plain `PreTrainedConfig`s, so modular adds nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
They run the Llama implementation, which Llama's own tests cover. The config-specific behaviour is covered by the fast tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The common tests re-ran Llama's suite with Llama-equivalent configs. TeleChat3's RoPE conversion is the only config logic worth a test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
[For maintainers] Suggested jobs to run (before merge) run-slow: auto, telechat, telechat3 |
…ched TeleChat RoPE - Restore the modular files of InternLM3, IQuestCoder and TeleChat3, inheriting from `LlamaConfig` - Revert the `Interleave` changes and add a TeleChat-only `TeleChatInterleaveKeyValue` op - Map TeleChat's original config keys explicitly instead of via `attribute_map` - Move `training_seqlen` and `base_seqlen` into `rope_parameters` (`"rope_type": "telechat_ntk"`) - Cache the TeleChat RoPE frequencies, recomputing them only when the length beyond training changes - Trim the TeleChat tests, use `Expectations` and `MemoryCleanupMixin`, drop the TeleChat3 config tests - Use note/warning callouts in the docs and drop the conversion paragraph from the TeleChat docs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This PR now only adds TeleChat. Also document TeleChat's RoPE parameters under `rope_parameters`, since the docstring checker drops free text from config docstrings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vasqu
left a comment
There was a problem hiding this comment.
Still not so happy with the rope, maybe @zucchini-nlp has a better idea here
Also best to check whether TP really works as expected here (meaning along the new interleave op)
| if "n_layer" in kwargs: | ||
| self.num_hidden_layers = kwargs.pop("n_layer") | ||
| if "n_head" in kwargs: | ||
| self.num_attention_heads = kwargs.pop("n_head") | ||
| if "ffn_hidden_size" in kwargs: | ||
| self.intermediate_size = kwargs.pop("ffn_hidden_size") | ||
| if "layer_norm_epsilon" in kwargs: | ||
| self.rms_norm_eps = kwargs.pop("layer_norm_epsilon") | ||
| if "seq_length" in kwargs: | ||
| self.max_position_embeddings = kwargs.pop("seq_length") | ||
| if self.max_position_embeddings is None: | ||
| self.max_position_embeddings = kwargs.get("training_seqlen", 8192) |
There was a problem hiding this comment.
can be simplified a bit but no biggie
There was a problem hiding this comment.
attribute map is better imo if we're just renaming stuff
There was a problem hiding this comment.
It used to be an attribute map but I was asked to remove it 😅
| # The original code supports these options, but no released checkpoint enables them | ||
| for option in ("embed_layernorm", "apply_residual_connection_post_layernorm"): | ||
| if getattr(self, option, False): | ||
| raise ValueError(f"TeleChat does not support `{option}`.") |
There was a problem hiding this comment.
This was done so that users (however rare they may be) coming from the custom code don't expect these custom code features to work
| raise ValueError(f"TeleChat does not support `{option}`.") | ||
|
|
||
|
|
||
| class TeleChatRotaryEmbedding(nn.Module): |
There was a problem hiding this comment.
hmm still not a super fan of this, ig we need to always compute the freq? Is that really needed
cc @zucchini-nlp if you have good ideas on introducing the custom rope here
There was a problem hiding this comment.
maybe we can do as in Hunyuan's ntk scaled rope
zucchini-nlp
left a comment
There was a problem hiding this comment.
we had a custom ntk rope with hunyuan so can accomodate imo, just needs a little tweaking to make rope module support all other types as well
| raise ValueError(f"TeleChat does not support `{option}`.") | ||
|
|
||
|
|
||
| class TeleChatRotaryEmbedding(nn.Module): |
There was a problem hiding this comment.
maybe we can do as in Hunyuan's ntk scaled rope
| # `_init_weights` recomputes the buffers with `compute_default_rope_parameters` | ||
| self.rope_type = "default" | ||
| inv_freq, _ = self.compute_default_rope_parameters(config) |
There was a problem hiding this comment.
nah we dont want to silently force default when it is not default under the hood
| if is_torchdynamo_compiling(): | ||
| # The cache lookup depends on the data, which can't be traced, so recompute the parameters instead | ||
| inv_freq, attention_scaling = self.compute_default_rope_parameters( | ||
| self.config, x.device, position_ids.max() + 1 | ||
| ) | ||
| else: | ||
| self.ntk_frequency_update(position_ids, x.device) | ||
| inv_freq, attention_scaling = self.inv_freq, self.attention_scaling |
There was a problem hiding this comment.
and here we could do the same
if not is_torchdynamo_compiling() and "ntk_custom_name" in rope_type:
self.ntk_frequency_update(position_ids, x.device)
# keep the rest same as Llama and use `self.inv_freq` as it was updated by above call| if "n_layer" in kwargs: | ||
| self.num_hidden_layers = kwargs.pop("n_layer") | ||
| if "n_head" in kwargs: | ||
| self.num_attention_heads = kwargs.pop("n_head") | ||
| if "ffn_hidden_size" in kwargs: | ||
| self.intermediate_size = kwargs.pop("ffn_hidden_size") | ||
| if "layer_norm_epsilon" in kwargs: | ||
| self.rms_norm_eps = kwargs.pop("layer_norm_epsilon") | ||
| if "seq_length" in kwargs: | ||
| self.max_position_embeddings = kwargs.pop("seq_length") | ||
| if self.max_position_embeddings is None: | ||
| self.max_position_embeddings = kwargs.get("training_seqlen", 8192) |
There was a problem hiding this comment.
attribute map is better imo if we're just renaming stuff
| def _validate_telechat_ntk_rope_parameters(self, rope_parameters: dict, ignore_keys: set[str] | None = None): | ||
| required_keys = {"rope_type", "rope_theta", "original_max_position_embeddings", "base_seqlen"} | ||
| self._check_received_keys("telechat_ntk", set(rope_parameters), required_keys, ignore_keys=ignore_keys) |
There was a problem hiding this comment.
validate_rope from here no? IF type==custom_nkt, do custom validation; else super().validate_rope
transformers/src/transformers/modeling_rope_utils.py
Lines 868 to 874 in 07cc8e8
Its fused key/value conversion runs on each rank's shard, which holds whole per-head key/value pairs, so the TP tests check that it shards correctly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its config has no dtype, but its weights are stored in bf16, so `dtype="auto"` loads them in bf16. The expected logits are re-recorded on B200; the generated text is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Python evaluates `dict.get`'s default before the lookup, so the pops nested in the defaults already ran and the trailing pops were dead. Pop the original keys once as defaults and build `rope_parameters` from them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Closing: vLLM is removing TeleChat2 and TeleChat3 (vllm-project/vllm#67035) because usage is very low, so native support here is no longer needed. |
Summary
Adds native support for TeleChat (v1, TeleChat2 and TeleChat2.5,
model_type: "telechat"). vLLM's native backend loads it into Llama's implementation, and this lets vLLM serve it through its Transformers backend without remote code. InternLM3 (#50578), IQuestCoder (#50579) and TeleChat3 (#50580) were split out of this PR.One model covers every checkpoint with
model_type: "telechat", except TeleChat-52B. That is a different architecture, and its config is rejected with a pointer totrust_remote_code. Differences from Llama:__init__s differ from Llama's. Theirforwards are Llama's.transformer.h.N.self_attention.{query,dense},word_embeddings,ln_f) are renamed. The fusedself_attention.key_valueis laid out per head ([k_0, v_0, k_1, v_1, …]). A TeleChat-onlyTeleChatInterleaveKeyValueop regroups it so thatChunkcan split it intok_projandv_proj.save_pretrainedwrites the original layout back, and base-model-only checkpoints (Tele-AI/telechat-7B) also load. vLLM's Transformers backend needs [Transformers backend] Apply Transformers'WeightConverters when loading weights vllm-project/vllm#66811 to apply the converter.n_layer,n_head,ffn_hidden_size,layer_norm_epsilon,seq_length) are mapped explicitly in__post_init__.rope_scalingis ignored, because the original code never reads it.training_seqlenandbase_seqlenmove intorope_parameters:Once the sequence outgrows
original_max_position_embeddings, the original code applies a stepwise NTK scaling.TeleChatRotaryEmbeddingreproduces it:The frequencies are cached in the
inv_freqbuffer, likedynamic_rope_update:Unlike
dynamic_rope_update, this rescales for the exact current length, as the original does, not the longest length seen so far.Tokenizers
The checkpoints'
tokenizer_config.jsonnamesTelechat2Tokenizer, which only exists in their remote code.telechatis added toMODELS_WITH_INCORRECT_HUB_TOKENIZER_CLASS, so the tokenizer loads asTokenizersBackendfrom the baretokenizer.modelwithouttrust_remote_code. That conversion only matches the original tokenizer with #50555. Merge #50555 first.Supersedes the stale v4-based #35694.
Evidence
Random tiny models built with each of the 5 versions of the original code. With
training_seqlen=64, a 200-token input puts every position past it (NTK alpha 15, mscale 1.11), and the cached greedy decode crosses it:telechat-7B(v1)TeleChat2-3BTeleChat2-115B(GQA)TeleChat2.5-35BTeleChat2.5-115B(GQA)All of the remaining difference is fp16 rounding. The original rounds cos/sin to fp16 before and after applying mscale, which the native implementation deliberately doesn't do. *The original v1 code crashes once cached decoding passes
training_seqlen, because its rotary embedding uses the chunk length rather than the total length. So that comparison covers the 5 steps it completes.Real weights:
Tele-AI/TeleChat2-3B(10K tokens > 8Ktraining_seqlen)Tele-AI/telechat-7B(v1, NTK alpha 3 at any length)Tele-AI/TeleChat2-7B-32K*The eager path of these versions of the original code only works in fp16.
TeleChat2-3B's Hub config loads asmax_position_embeddings=32768andrope_parameters={"rope_type": "telechat_ntk", "rope_theta": 10000.0, "original_max_position_embeddings": 8192, "base_seqlen": 8192}.RUN_SLOW=1integration tests (logits and generation forTeleChat2-3B, defaultdtype="auto", which loads the bf16 weights): 2 passed on B200, after the RoPE caching change.TeleChatInterleaveKeyValuehas a split-and-merge round-trip unit test.Merge Danger
Door: two-way
A new model type, a new
telechatentry inconversion_mappingand a new TeleChat-only conversion op. Depends on #50555 for tokenization. For vLLM, TeleChat also needs vllm-project/vllm#66811.Blast Radius: new model
Loading these checkpoints without
trust_remote_codenow resolves to the native config, model and tokenizer, where it previously required remote code. Withtrust_remote_code=True,auto_mapstill takes precedence. For TeleChat-52B (samemodel_type), the config now fails with an explicit error instead of a remote-code prompt, unlesstrust_remote_code=Trueis passed.🤖 Generated with Claude Code