Files
wehub-resource-sync 59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:31 +08:00

739 lines
28 KiB
Python

# Copyright (c) 2026 LightSeek Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Model configuration helpers and derived runtime metadata."""
import copy
import json
import math
import os
from collections.abc import Callable
from dataclasses import dataclass
from enum import IntEnum, auto
import torch
import yaml
from transformers import PretrainedConfig
from tokenspeed.runtime.layers.quantization import QUANTIZATION_METHODS
from tokenspeed.runtime.utils import get_colorful_logger
from tokenspeed.runtime.utils.env import envs
from tokenspeed.runtime.utils.hf_transformers_utils import (
get_config,
get_context_length,
get_generation_config,
resolve_architecture,
)
from tokenspeed.runtime.utils.server_args import ServerArgs
logger = get_colorful_logger(__name__)
_DEEPSEEK_V4_ARCHITECTURES = frozenset(
{
"DeepseekV4ForCausalLM",
"DeepseekV4ForCausalLMNextN",
}
)
_MLA_ARCHITECTURES = frozenset(
{
"DeepseekV3ForCausalLM",
"DeepseekV3ForCausalLMNextN",
"Eagle3DeepseekV2ForCausalLM",
"LongcatFlashForCausalLM",
"KimiK25ForConditionalGeneration",
}
)
_DSA_ARCHITECTURES = frozenset(
{
"GlmMoeDsaForCausalLM",
"GlmMoeDsaForCausalLMNextN",
}
)
_DOUBLE_ATTENTION_LAYER_ARCHITECTURES = frozenset(
{
"LongcatFlashForCausalLM",
}
)
class AttentionArch(IntEnum):
MLA = auto()
MHA = auto()
DSA = auto()
@dataclass(frozen=True)
class _AttentionFamilySpec:
name: str
architectures: frozenset[str]
configure: Callable[[object], None]
default_backend: str | None = None
supports_target_verify_forward_mode: bool = False
default_block_size: int | None = None
def override_model_config(model_config, ext_yaml):
with open(ext_yaml, encoding="utf-8") as f:
ext_config = yaml.safe_load(f)
override_model_config: dict = ext_config.get("override_model_config", {})
for k, v in override_model_config.items():
if hasattr(model_config, k):
old_v = model_config.__getattribute__(k)
if isinstance(v, dict):
new_v = copy.deepcopy(old_v)
new_v.__dict__.update(v)
else:
new_v = v
model_config.__setattr__(k, new_v)
logger.info("Override model config: %s=%r", k, new_v)
def is_deepseek_v4(config: PretrainedConfig) -> bool:
return resolve_architecture(config) in _DEEPSEEK_V4_ARCHITECTURES
def is_deepseek_v4_nextn(config: PretrainedConfig) -> bool:
return resolve_architecture(config) == "DeepseekV4ForCausalLMNextN"
def configure_deepseek_v4_attention(model_config) -> None:
"""Derive DeepSeek V4's MLA-like dimensions for runtime setup."""
hf_config = model_config.hf_config
model_config.head_dim = hf_config.head_dim
model_config.attention_arch = AttentionArch.MLA
model_config.kv_lora_rank = hf_config.head_dim
model_config.qk_rope_head_dim = hf_config.qk_rope_head_dim
model_config.qk_nope_head_dim = hf_config.head_dim - hf_config.qk_rope_head_dim
model_config.v_head_dim = hf_config.head_dim
model_config.index_head_dim = getattr(hf_config, "index_head_dim", None)
model_config.scaling = 1 / math.sqrt(model_config.head_dim)
rope_scaling = getattr(hf_config, "rope_scaling", None)
if rope_scaling:
mscale_all_dim = rope_scaling.get("mscale_all_dim", False)
scaling_factor = rope_scaling["factor"]
mscale = yarn_get_mscale(scaling_factor, float(mscale_all_dim))
model_config.scaling = model_config.scaling * mscale * mscale
def configure_glm_attention(model_config) -> None:
mla_config = (
model_config.hf_text_config
if hasattr(model_config.hf_text_config, "kv_lora_rank")
else model_config.hf_config
)
required_fields = (
"kv_lora_rank",
"qk_nope_head_dim",
"qk_rope_head_dim",
"v_head_dim",
"index_topk",
"index_head_dim",
"index_n_heads",
)
missing_fields = [
field for field in required_fields if not hasattr(mla_config, field)
]
if missing_fields:
raise ValueError(
"GLM attention config is missing required fields: "
+ ", ".join(missing_fields)
)
model_config.head_dim = getattr(mla_config, "qk_head_dim", None)
if model_config.head_dim is None:
model_config.head_dim = (
mla_config.qk_nope_head_dim + mla_config.qk_rope_head_dim
)
model_config.attention_arch = AttentionArch.DSA
model_config.kv_lora_rank = mla_config.kv_lora_rank
model_config.qk_nope_head_dim = mla_config.qk_nope_head_dim
model_config.qk_rope_head_dim = mla_config.qk_rope_head_dim
model_config.v_head_dim = mla_config.v_head_dim
model_config.index_topk = mla_config.index_topk
model_config.index_head_dim = mla_config.index_head_dim
model_config.index_n_heads = mla_config.index_n_heads
model_config.index_topk_pattern = getattr(mla_config, "index_topk_pattern", None)
model_config.scaling = 1 / math.sqrt(
model_config.qk_nope_head_dim + model_config.qk_rope_head_dim
)
rope_scaling = getattr(mla_config, "rope_scaling", None)
if rope_scaling and "factor" in rope_scaling:
mscale_all_dim = rope_scaling.get("mscale_all_dim", False)
scaling_factor = rope_scaling["factor"]
mscale = yarn_get_mscale(scaling_factor, float(mscale_all_dim))
model_config.scaling = model_config.scaling * mscale * mscale
def configure_mla_attention(model_config) -> None:
mla_config = (
model_config.hf_text_config
if hasattr(model_config.hf_text_config, "kv_lora_rank")
else model_config.hf_config
)
model_config.head_dim = 256
model_config.attention_arch = AttentionArch.MLA
model_config.kv_lora_rank = mla_config.kv_lora_rank
model_config.qk_nope_head_dim = mla_config.qk_nope_head_dim
model_config.qk_rope_head_dim = mla_config.qk_rope_head_dim
model_config.v_head_dim = mla_config.v_head_dim
model_config.scaling = 1 / math.sqrt(
model_config.qk_nope_head_dim + model_config.qk_rope_head_dim
)
rope_scaling = getattr(mla_config, "rope_scaling", None)
if rope_scaling and "factor" in rope_scaling:
mscale_all_dim = rope_scaling.get("mscale_all_dim", False)
scaling_factor = rope_scaling["factor"]
mscale = yarn_get_mscale(scaling_factor, float(mscale_all_dim))
model_config.scaling = model_config.scaling * mscale * mscale
_ATTENTION_FAMILY_SPECS = (
_AttentionFamilySpec(
name="DeepSeek V4",
architectures=_DEEPSEEK_V4_ARCHITECTURES,
configure=configure_deepseek_v4_attention,
supports_target_verify_forward_mode=True,
default_block_size=256,
),
_AttentionFamilySpec(
name="GLM",
architectures=_DSA_ARCHITECTURES,
configure=configure_glm_attention,
default_backend="dsa",
supports_target_verify_forward_mode=True,
),
_AttentionFamilySpec(
name="MLA",
architectures=_MLA_ARCHITECTURES,
configure=configure_mla_attention,
),
)
def _model_architectures(
hf_config: PretrainedConfig,
hf_text_config: PretrainedConfig,
) -> list[str]:
return (
[resolve_architecture(hf_config)]
+ list(getattr(hf_config, "architectures", None) or [])
+ list(getattr(hf_text_config, "architectures", []) or [])
)
def _resolve_attention_family(
hf_config: PretrainedConfig,
hf_text_config: PretrainedConfig,
) -> _AttentionFamilySpec | None:
architectures = _model_architectures(hf_config, hf_text_config)
for spec in _ATTENTION_FAMILY_SPECS:
if any(arch in spec.architectures for arch in architectures):
return spec
return None
def _apply_attention_family_defaults(
server_args: ServerArgs,
spec: _AttentionFamilySpec,
) -> None:
if spec.default_block_size is not None:
block_size_default = ServerArgs.__dataclass_fields__["block_size"].default
if server_args.block_size == block_size_default:
logger.info(
"%s default block_size=%d; pass --block-size with a value other "
"than %d to keep that value.",
spec.name,
spec.default_block_size,
block_size_default,
)
server_args.block_size = spec.default_block_size
if spec.default_backend is not None and server_args.attention_backend is None:
server_args.attention_backend = spec.default_backend
def _derive_num_attention_layers(
hf_config: PretrainedConfig,
num_hidden_layers: int,
) -> int:
architectures = getattr(hf_config, "architectures", None) or []
num_attention_layers = num_hidden_layers
if is_deepseek_v4_nextn(hf_config):
num_attention_layers = int(getattr(hf_config, "num_nextn_predict_layers", 1))
if any(arch in _DOUBLE_ATTENTION_LAYER_ARCHITECTURES for arch in architectures):
num_attention_layers = num_hidden_layers * 2
return num_attention_layers
class ModelConfig:
def __init__(
self,
model_path: str,
trust_remote_code: bool = True,
revision: str | None = None,
context_length: int | None = None,
model_override_args: dict | None = None,
dtype: str = "auto",
quantization: str | None = None,
override_config_file: str | None = None,
is_draft_worker: bool | None = False,
server_args: ServerArgs = None,
) -> None:
self.model_path = model_path
self.revision = revision
self.quantization = quantization
self.mapping = server_args.mapping
# Parse args
self.model_override_args = json.loads(model_override_args)
kwargs = {}
if override_config_file and override_config_file.strip():
kwargs["_configuration_file"] = override_config_file.strip()
self.hf_config = get_config(
model_path,
trust_remote_code=trust_remote_code,
revision=revision,
model_override_args=self.model_override_args,
is_draft_worker=is_draft_worker,
**kwargs,
)
self.hf_generation_config = get_generation_config(
self.model_path,
trust_remote_code=trust_remote_code,
revision=revision,
**kwargs,
)
self.hf_text_config = get_hf_text_config(self.hf_config)
# Check model type
self.is_generation = is_generation_model(self.hf_config.architectures)
self.is_multimodal = is_multimodal_model(self.hf_config.architectures)
self.is_multimodal_gen = is_multimodal_gen_model(self.hf_config.architectures)
self.is_image_gen = is_image_gen_model(self.hf_config.architectures)
self.is_audio_model = is_audio_model(self.hf_config.architectures)
language_model_only = bool(getattr(server_args, "language_model_only", False))
# Target-only flag; never apply to draft / auxiliary checkpoints.
apply_language_model_only = language_model_only and not is_draft_worker
if apply_language_model_only:
if not self.is_multimodal:
raise ValueError(
"--language-model-only requires a multimodal model checkpoint."
)
logger.info(
"Running in language-model-only mode: vision/audio encoders will "
"be skipped; requests with multimodal inputs will be rejected."
)
# ``is_multimodal`` is the architectural fact; this is the runtime gate.
self.is_multimodal_active = self.is_multimodal and not apply_language_model_only
# Vision-only role (EPD encode): the inverse axis of language_model_only.
# Build the vision tower (is_multimodal_active stays True) but SKIP LM
# construction + LM weight load so a full ViT fits at encode TP=1.
encoder_only = (
getattr(server_args, "disaggregation_mode", None) == "encode"
and not is_draft_worker
)
if encoder_only and not self.is_multimodal:
raise ValueError(
"disaggregation_mode=encode requires a multimodal checkpoint."
)
if encoder_only and apply_language_model_only:
raise ValueError(
"disaggregation_mode=encode (encoder-only) and language_model_only "
"are mutually exclusive."
)
if encoder_only and self.is_audio_model:
raise ValueError(
"disaggregation_mode=encode does not support audio models; "
"only image/video encoders are currently supported."
)
if encoder_only:
# Single model-facing gate: Kimi reads hf_config.encoder_only directly;
# Qwen3_5ForConditionalGeneration reads it to skip LM construction.
self.hf_config.encoder_only = True
logger.info(
"Running in encoder-only mode: the language model will not "
"be constructed or loaded (encode role)."
)
# Cap gpu_memory_utilization for VLMs in mm mode — the vision encoder
# needs headroom that the global default doesn't account for.
if (
self.is_multimodal_active
and getattr(server_args, "_gpu_memory_utilization_defaulted", False)
and server_args.gpu_memory_utilization > 0.9
):
logger.info(
"Clamping gpu_memory_utilization %.2f -> 0.9 to leave headroom "
"for the vision encoder.",
server_args.gpu_memory_utilization,
)
server_args.gpu_memory_utilization = 0.9
self.mm_attention_backend = getattr(server_args, "mm_attention_backend", None)
self.dtype = _get_and_verify_dtype(self.hf_text_config, dtype)
# Derive context length
derived_context_len = get_context_length(self.hf_text_config)
if context_length is not None:
if context_length > derived_context_len:
if envs.TOKENSPEED_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN.get():
logger.warning(
"User-specified context_length (%s) is greater than the derived "
"context_length (%s). This may lead to incorrect model outputs or "
"CUDA errors.",
context_length,
derived_context_len,
)
self.context_len = context_length
else:
raise ValueError(
f"User-specified context_length ({context_length}) is greater than the derived context_length ({derived_context_len}). "
f"This may lead to incorrect model outputs or CUDA errors. Note that the derived context_length may differ from max_position_embeddings in the model's config. "
f"To allow overriding this maximum, set the env var TOKENSPEED_ALLOW_OVERWRITE_LONGER_CONTEXT_LEN=1"
)
else:
self.context_len = context_length
else:
self.context_len = derived_context_len
# Unify the config keys for hf_text_config
self.head_dim = getattr(
self.hf_text_config,
"head_dim",
self.hf_text_config.hidden_size // self.hf_text_config.num_attention_heads,
)
# MLA/DSA families carry per-head dimension metadata that does not
# follow the standard hidden_size / num_attention_heads derivation above.
attention_family = _resolve_attention_family(
self.hf_config,
self.hf_text_config,
)
if attention_family is not None:
_apply_attention_family_defaults(server_args, attention_family)
attention_family.configure(self)
elif "MiniCPM3ForCausalLM" in self.hf_config.architectures:
self.head_dim = 128
self.attention_arch = AttentionArch.MLA
self.kv_lora_rank = self.hf_config.kv_lora_rank
self.qk_rope_head_dim = self.hf_config.qk_rope_head_dim
else:
self.attention_arch = AttentionArch.MHA
self.use_v4_mtp_paged_metadata = (
getattr(server_args, "speculative_algorithm", None) is not None
and not is_draft_worker
and attention_family is not None
and attention_family.supports_target_verify_forward_mode
)
self.num_attention_heads = self.hf_text_config.num_attention_heads
self.num_key_value_heads = getattr(
self.hf_text_config, "num_key_value_heads", None
)
# for Dbrx and MPT models
if self.hf_config.model_type in {"dbrx", "mpt"}:
self.num_key_value_heads = getattr(
self.hf_config.attn_config, "kv_n_heads", None
)
if self.num_key_value_heads is None:
self.num_key_value_heads = self.num_attention_heads
self.hidden_size = self.hf_text_config.hidden_size
self.num_hidden_layers = getattr(self.hf_text_config, "num_hidden_layers", None)
if self.num_hidden_layers is None:
self.num_hidden_layers = self.hf_text_config.num_layers
self.num_attention_layers = _derive_num_attention_layers(
self.hf_config,
self.num_hidden_layers,
)
if is_draft_worker:
mtp_layers = getattr(self.hf_text_config, "mtp_num_hidden_layers", None)
if mtp_layers is not None:
self.num_attention_layers = mtp_layers
else:
nextn_layers = getattr(
self.hf_text_config, "num_nextn_predict_layers", None
)
if nextn_layers is not None and nextn_layers > 0:
self.num_attention_layers = nextn_layers
self.vocab_size = self.hf_text_config.vocab_size
# Verify quantization
self._verify_quantization()
# Cache attributes
self.hf_eos_token_id = self.get_hf_eos_token_id()
self.image_token_id = getattr(self.hf_config, "image_token_id", None)
if server_args is not None and server_args.load_format == "extensible":
override_model_config(self, server_args.ext_yaml)
def _parse_quant_hf_config(self):
quant_cfg = getattr(self.hf_config, "quantization_config", None)
if quant_cfg is None:
# compressed-tensors uses a "compression_config" key
quant_cfg = getattr(self.hf_config, "compression_config", None)
if quant_cfg is None:
# modelopt NVFP4 checkpoints store quant config in hf_quant_config.json
# Resolve the local snapshot directory (model_path may be a HF hub ID)
if os.path.isdir(self.model_path):
model_dir = self.model_path
else:
try:
from huggingface_hub import snapshot_download
model_dir = snapshot_download(
self.model_path,
revision=self.revision,
allow_patterns=["*.json"],
local_files_only=True,
)
except Exception as exc:
logger.debug(
"Unable to resolve local quantization config for %s: %s",
self.model_path,
exc,
)
model_dir = None
if model_dir is not None:
hf_quant_path = os.path.join(model_dir, "hf_quant_config.json")
if os.path.isfile(hf_quant_path):
with open(hf_quant_path, encoding="utf-8") as f:
hf_quant = json.load(f)
quant_algo = hf_quant.get("quantization", {}).get("quant_algo", "")
if quant_algo:
quant_cfg = {
"quant_method": "modelopt",
"quant_algo": quant_algo,
}
quant_cfg.update(hf_quant.get("quantization", {}))
return quant_cfg
def _verify_quantization(self) -> None:
supported_quantization = [*QUANTIZATION_METHODS]
optimized_quantization_methods = [
"fp8",
"nvfp4",
"mxfp4",
"compressed_tensors",
"compressed-tensors",
"w8a8_fp8",
]
compatible_quantization_methods = {
"w8a8_fp8": ["compressed-tensors", "compressed_tensors"],
}
if self.quantization is not None:
self.quantization = self.quantization.lower()
# Parse quantization method from the HF model config, if available.
quant_cfg = self._parse_quant_hf_config()
if quant_cfg is not None:
quant_method = quant_cfg.get("quant_method", "").lower()
# Detect which checkpoint is it
for _, method in QUANTIZATION_METHODS.items():
quantization_override = method.override_quantization_method(
quant_cfg, self.quantization
)
if quantization_override:
quant_method = quantization_override
self.quantization = quantization_override
break
# Verify quantization configurations.
if self.quantization is None:
self.quantization = quant_method
elif self.quantization != quant_method:
if (
self.quantization not in compatible_quantization_methods
or quant_method
not in compatible_quantization_methods[self.quantization]
):
raise ValueError(
"Quantization method specified in the model config "
f"({quant_method}) does not match the quantization "
f"method specified in the `quantization` argument "
f"({self.quantization})."
)
if self.quantization is not None:
if self.quantization not in supported_quantization:
raise ValueError(
f"Unknown quantization method: {self.quantization}. Must "
f"be one of {supported_quantization}."
)
if self.quantization not in optimized_quantization_methods:
logger.warning(
"%s quantization is not fully "
"optimized yet. The speed can be slower than "
"non-quantized models.",
self.quantization,
)
def get_hf_eos_token_id(self) -> set[int] | None:
eos_ids = getattr(self.hf_config, "eos_token_id", None)
if eos_ids:
# it can be either int or list of int
eos_ids = {eos_ids} if isinstance(eos_ids, int) else set(eos_ids)
if eos_ids is None:
eos_ids = set()
if self.hf_generation_config:
generation_eos_ids = getattr(
self.hf_generation_config, "eos_token_id", None
)
if generation_eos_ids:
generation_eos_ids = (
{generation_eos_ids}
if isinstance(generation_eos_ids, int)
else set(generation_eos_ids)
)
eos_ids = eos_ids | generation_eos_ids
return eos_ids
def get_hf_text_config(config: PretrainedConfig):
"""Get the "sub" config relevant to llm for multi modal models.
No op for pure text models.
"""
class_name = resolve_architecture(config)
if class_name.startswith("Llava") and class_name.endswith("ForCausalLM"):
# We support non-hf version of llava models, so we do not want to
# read the wrong values from the unused default text_config.
# We set `dtype` of config to `torch.float16` for the weights, as
# `torch.float16` is default used for image features in
# `python/tokenspeed/runtime/models/llava.py`.
config.dtype = torch.float16
return config
if hasattr(config, "thinker_config"):
thinker_config = config.thinker_config
if hasattr(thinker_config, "text_config"):
return thinker_config.text_config
return thinker_config
if hasattr(config, "text_config"):
if not hasattr(config.text_config, "num_attention_heads"):
raise ValueError("text_config must define num_attention_heads")
return config.text_config
return config
_STR_DTYPE_TO_TORCH_DTYPE = {
"half": torch.float16,
"float16": torch.float16,
"float": torch.float32,
"float32": torch.float32,
"bfloat16": torch.bfloat16,
}
def _get_and_verify_dtype(
config: PretrainedConfig,
dtype: str | torch.dtype,
) -> torch.dtype:
# config.dtype can be missing or None.
config_dtype = getattr(config, "dtype", None)
if config_dtype is None:
config_dtype = torch.bfloat16
if isinstance(dtype, str):
dtype = dtype.lower()
if dtype == "auto":
if config_dtype == torch.float32:
if config.model_type == "gemma2":
logger.info(
"For Gemma 2, we downcast float32 to bfloat16 instead "
"of float16 by default. Please specify `dtype` if you "
"want to use float16."
)
torch_dtype = torch.bfloat16
else:
# Following the common practice, we use float16 for float32
# models.
torch_dtype = torch.float16
else:
torch_dtype = config_dtype
else:
if dtype not in _STR_DTYPE_TO_TORCH_DTYPE:
raise ValueError(f"Unknown dtype: {dtype}")
torch_dtype = _STR_DTYPE_TO_TORCH_DTYPE[dtype]
elif isinstance(dtype, torch.dtype):
torch_dtype = dtype
else:
raise ValueError(f"Unknown dtype: {dtype}")
# Verify the dtype.
if torch_dtype != config_dtype:
if torch_dtype == torch.float32:
# Upcasting to float32 is allowed.
logger.info("Upcasting %s to %s.", config_dtype, torch_dtype)
elif config_dtype == torch.float32:
# Downcasting from float32 to float16 or bfloat16 is allowed.
logger.info("Downcasting %s to %s.", config_dtype, torch_dtype)
else:
# Casting between float16 and bfloat16 is allowed with a warning.
logger.warning("Casting %s to %s.", config_dtype, torch_dtype)
return torch_dtype
def is_generation_model(model_architectures: list[str]):
return True
def is_multimodal_model(model_architectures: list[str] | None):
multimodal_architectures = {
"Qwen3_5ForConditionalGeneration",
"Qwen3_5MoeForConditionalGeneration",
"Qwen3OmniMoeForConditionalGeneration",
"Qwen3ASRForConditionalGeneration",
"KimiK25ForConditionalGeneration",
}
return any(arch in multimodal_architectures for arch in model_architectures or [])
def is_multimodal_gen_model(model_architectures: list[str]):
return False
def is_image_gen_model(model_architectures: list[str]):
return False
def is_audio_model(model_architectures: list[str] | None):
audio_architectures = {
"Qwen3OmniMoeForConditionalGeneration",
"Qwen3ASRForConditionalGeneration",
}
return any(arch in audio_architectures for arch in model_architectures or [])
def yarn_get_mscale(scale: float = 1, mscale: float = 1) -> float:
if scale <= 1:
return 1.0
return 0.1 * mscale * math.log(scale) + 1.0