Text Generation
Safetensors
Transformers
English
Russian
Ukrainian
vllm
qwen3_5
image-text-to-text
long-context
1m-context
million-token-context
context-extension
needle-in-a-haystack
retrieval
retrieval-heads
consumer-gpu
single-gpu
rtx-5090
rtx-4090
quantization
nvfp4
3-bit
fp8
int8
kv-cache-quantization
turboquant
3-bit-kv-cache
hybrid-architecture
linear-attention
gated-deltanet
state-space
gqa
multimodal
vision-language
conversational
agentic
coding
roleplay
russian
ukrainian
custom_code
measured-benchmarks
Eval Results (legacy)
8-bit precision
compressed-tensors
Instructions to use Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV", trust_remote_code=True) messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoProcessor, AutoModelForImageTextToText processor = AutoProcessor.from_pretrained("Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV", trust_remote_code=True) model = AutoModelForImageTextToText.from_pretrained("Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV", trust_remote_code=True, device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV
- SGLang
How to use Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV with Docker Model Runner:
docker model run hf.co/Ddavidich/LOMONOSOV-ZENIT-27B-1M-INDEV
| """ALTAY для transformers: `from_pretrained` без vLLM. | |
| До появления этого файла путь через transformers не работал вовсе. Загрузчик | |
| откатывался на стоковый `qwen3_5`, который не знает ни упакованных в восемь бит | |
| эмбеддингов, ни оверлея ALTAY, и падал на `'Embedding' object has no attribute | |
| 'weight'`. | |
| Здесь воспроизведены обе недостающие части, и воспроизведены **целиком**. | |
| Соблазн собрать урезанную версию был велик — множители оверлея малы, от −0.014 до | |
| +0.023, и казалось, что без него «почти то же самое». Так делать нельзя: под | |
| кнопкой «Use this model» оказалась бы модель, отвечающая иначе, чем отгружаемая. | |
| Либо честно, либо никак. | |
| Что такое оверлей. Двадцать восемь тензоров, два суперблока. Суперблок — это три | |
| рекуррентных повтора и один повтор внимания подряд. Повтор берёт **чужой слой | |
| целиком** — его нормировку, смеситель и MLP, с теми же весами, — прогоняет через | |
| него состояние ещё раз и добавляет результат с обучаемым множителем | |
| `altay_alpha`. Сверх этого к запросу и выходу внимания добавляются низкоранговые | |
| дельты ранга 64. | |
| суперблок A: повторяет слои 28, 29, 30 (рекуррентные) и 31 (внимание), | |
| выполняется сразу после слоя 31; | |
| суперблок B: повторяет слои 60, 61, 62 и 63, выполняется после слоя 63. | |
| Приватное состояние. Рекуррентные повторы обязаны иметь **своё** состояние, а не | |
| делить его с исходным слоем: в vLLM для этого заведены шесть дополнительных | |
| ячеек кэша. Здесь та же вещь сделана вторым объектом кэша — веса общие, состояния | |
| раздельные. | |
| Чего этот файл НЕ даёт по сравнению с путём через vLLM: кодека TurboQuant для | |
| кэша KV, профилей запуска и ступенчатой карты позиций. На окнах до 196 608 | |
| токенов карта позиций тождественна, поэтому там разницы нет; дальше — есть, и | |
| для длинного контекста надо брать vLLM. | |
| """ | |
| from __future__ import annotations | |
| import torch | |
| from torch import nn | |
| from torch.nn import functional as F | |
| from transformers.masking_utils import create_causal_mask | |
| from transformers.models.qwen3_5.modeling_qwen3_5 import ( | |
| Qwen3_5ForConditionalGeneration, | |
| Qwen3_5Model, | |
| Qwen3_5ModelOutputWithPast, | |
| Qwen3_5TextModel, | |
| apply_rotary_pos_emb, | |
| ) | |
| try: # имя менялось между версиями transformers | |
| from transformers.masking_utils import create_recurrent_attention_mask | |
| except ImportError: # pragma: no cover | |
| create_recurrent_attention_mask = create_causal_mask | |
| OVERLAY_RANK = 64 | |
| SUPERBLOCK_A_RECURRENT = (28, 29, 30) | |
| SUPERBLOCK_A_ATTENTION = 31 | |
| SUPERBLOCK_B_RECURRENT = (60, 61, 62) | |
| SUPERBLOCK_B_ATTENTION = 63 | |
| def _linear(in_features: int, out_features: int, reference: torch.Tensor) -> nn.Linear: | |
| layer = nn.Linear(in_features, out_features, bias=False) | |
| return layer.to(device=reference.device, dtype=reference.dtype) | |
| class AltayRecurrentReplay(nn.Module): | |
| """Повтор рекуррентного слоя с приватным состоянием и дельтой ранга 64.""" | |
| def __init__(self, source_layer: nn.Module, hidden_size: int) -> None: | |
| super().__init__() | |
| # Слой-источник не регистрируется как подмодуль: его веса уже есть в | |
| # чекпойнте по своему адресу, и второй адрес сломал бы загрузку. | |
| object.__setattr__(self, "_source", source_layer) | |
| reference = source_layer.input_layernorm.weight | |
| self.altay_alpha = nn.Parameter( | |
| torch.zeros((), device=reference.device, dtype=torch.float32) | |
| ) | |
| self.delta_down = _linear(hidden_size, OVERLAY_RANK, reference) | |
| self.delta_up = _linear(OVERLAY_RANK, hidden_size, reference) | |
| def source(self) -> nn.Module: | |
| return object.__getattribute__(self, "_source") | |
| def forward(self, hidden_states, *, attention_mask, replay_cache, **kwargs): | |
| layer = self.source | |
| normalized = layer.input_layernorm(hidden_states) | |
| mixed = layer.linear_attn( | |
| hidden_states=normalized, | |
| cache_params=replay_cache, | |
| attention_mask=attention_mask, | |
| **kwargs, | |
| ) | |
| transformed = hidden_states + mixed | |
| transformed = transformed + layer.mlp(layer.post_attention_layernorm(transformed)) | |
| delta = self.delta_up(F.silu(self.delta_down(hidden_states))) | |
| branch = transformed - hidden_states + delta | |
| return hidden_states + self.altay_alpha.to(hidden_states.dtype) * branch | |
| class AltayAttentionReplay(nn.Module): | |
| """Повтор слоя внимания: свежие запрос и выход поверх тех же ключей.""" | |
| def __init__(self, source_layer: nn.Module, hidden_size: int) -> None: | |
| super().__init__() | |
| object.__setattr__(self, "_source", source_layer) | |
| attention = source_layer.self_attn | |
| reference = source_layer.input_layernorm.weight | |
| qg_width = attention.q_proj.out_features # запрос и вентиль вместе | |
| o_input = attention.o_proj.in_features | |
| self.altay_alpha = nn.Parameter( | |
| torch.zeros((), device=reference.device, dtype=torch.float32) | |
| ) | |
| self.q_delta_down = _linear(hidden_size, OVERLAY_RANK, reference) | |
| self.q_delta_up = _linear(OVERLAY_RANK, qg_width, reference) | |
| self.o_delta_down = _linear(o_input, OVERLAY_RANK, reference) | |
| self.o_delta_up = _linear(OVERLAY_RANK, hidden_size, reference) | |
| def source(self) -> nn.Module: | |
| return object.__getattribute__(self, "_source") | |
| def forward(self, hidden_states, *, position_embeddings, attention_mask, | |
| position_ids=None, **kwargs): | |
| from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS | |
| from transformers.models.qwen3_5.modeling_qwen3_5 import eager_attention_forward | |
| layer = self.source | |
| attention = layer.self_attn | |
| head_dim = attention.head_dim | |
| normalized = layer.input_layernorm(hidden_states) | |
| input_shape = normalized.shape[:-1] | |
| hidden_shape = (*input_shape, -1, head_dim) | |
| # Дельта ложится на запрос ДО нормировки головы и до поворота позиций — | |
| # именно там она обучалась. | |
| qg = attention.q_proj(normalized) | |
| qg = qg + self.q_delta_up(F.silu(self.q_delta_down(normalized))) | |
| query, gate = torch.chunk(qg.view(*input_shape, -1, head_dim * 2), 2, dim=-1) | |
| gate = gate.reshape(*input_shape, -1) | |
| query = attention.q_norm(query.view(hidden_shape)).transpose(1, 2) | |
| key = attention.k_norm(attention.k_proj(normalized).view(hidden_shape)).transpose(1, 2) | |
| value = attention.v_proj(normalized).view(hidden_shape).transpose(1, 2) | |
| cos, sin = position_embeddings | |
| query, key = apply_rotary_pos_emb(query, key, cos, sin) | |
| # Кэш не трогаем: повтор считает по своим ключам и значениям и ничего | |
| # в общий кэш не пишет, иначе он испортил бы основной проход. | |
| interface = ALL_ATTENTION_FUNCTIONS.get_interface( | |
| attention.config._attn_implementation, eager_attention_forward) | |
| raw, _ = interface(attention, query, key, value, attention_mask, | |
| dropout=0.0, scaling=attention.scaling) | |
| raw = raw.reshape(*input_shape, -1).contiguous() | |
| mixed = attention.o_proj(raw * torch.sigmoid(gate)) | |
| mixed = mixed + self.o_delta_up(F.silu(self.o_delta_down(raw))) | |
| transformed = hidden_states + mixed | |
| transformed = transformed + layer.mlp(layer.post_attention_layernorm(transformed)) | |
| branch = transformed - hidden_states | |
| return hidden_states + self.altay_alpha.to(hidden_states.dtype) * branch | |
| class AltayOverlay(nn.Module): | |
| """Двадцать восемь тензоров оверлея: два суперблока по четыре повтора.""" | |
| def __init__(self, text_model: Qwen3_5TextModel) -> None: | |
| super().__init__() | |
| hidden = text_model.config.hidden_size | |
| self.sb_a_recurrent = nn.ModuleList( | |
| [AltayRecurrentReplay(text_model.layers[i], hidden) | |
| for i in SUPERBLOCK_A_RECURRENT]) | |
| self.sb_a_attention = AltayAttentionReplay( | |
| text_model.layers[SUPERBLOCK_A_ATTENTION], hidden) | |
| self.sb_b_recurrent = nn.ModuleList( | |
| [AltayRecurrentReplay(text_model.layers[i], hidden) | |
| for i in SUPERBLOCK_B_RECURRENT]) | |
| self.sb_b_attention = AltayAttentionReplay( | |
| text_model.layers[SUPERBLOCK_B_ATTENTION], hidden) | |
| def run(self, name: str, hidden_states, *, recurrent_mask, attention_mask, | |
| position_embeddings, position_ids, replay_cache, **kwargs): | |
| for replay in getattr(self, f"{name}_recurrent"): | |
| hidden_states = replay(hidden_states, attention_mask=recurrent_mask, | |
| replay_cache=replay_cache, **kwargs) | |
| return getattr(self, f"{name}_attention")( | |
| hidden_states, position_embeddings=position_embeddings, | |
| attention_mask=attention_mask, position_ids=position_ids, **kwargs) | |
| class LomonosovZenitAltayTextModel(Qwen3_5TextModel): | |
| """Стоковая текстовая модель плюс оверлей после слоёв 31 и 63.""" | |
| def __init__(self, config): | |
| super().__init__(config) | |
| self.altay_overlay = AltayOverlay(self) | |
| # ---- упакованные в восемь бит эмбеддинги ------------------------------- | |
| def materialize_packed_embedding(self) -> None: | |
| """Развернуть embed_tokens ПОСЛЕ загрузки весов. | |
| Прежде здесь стоял пре-хук `_register_load_state_dict_pre_hook`, | |
| собиравший `weight` из пары `weight_packed`/`weight_scale` прямо в | |
| словаре состояния. Замерено 26.07.2026, что он не работает и работать не | |
| может по двум причинам сразу. | |
| Во-первых, compressed-tensors 0.17 обрабатывает `nn.Embedding` наравне с | |
| `nn.Linear`: при подготовке модуля он сам снимает `weight` и заводит | |
| `weight_packed`, `weight_scale`, `weight_shape` как параметры. Значит | |
| собирать `weight` из словаря состояния не нужно — упакованные тензоры | |
| загружаются штатно, MISSING нет ни одного. Не хватает только обратного | |
| хода, потому что сжатого прохода вперёд у эмбеддинга нет. | |
| Во-вторых, хук обращался к `module.weight.dtype` — к тому самому | |
| атрибуту, отсутствие которого он и должен был лечить. | |
| Поэтому распаковываем после загрузки и распаковщиком самой библиотеки, | |
| не повторяя её раскладку байтов у себя. Цена замерена: 16.96 ГБ весов | |
| превращаются в 18.23 ГБ, то есть развёрнутая таблица стоит 1.27 ГБ. | |
| Это путь совместимости, а не путь скорости: vLLM держит эмбеддинги | |
| упакованными и этой надбавки не платит. | |
| """ | |
| emb = getattr(self, "embed_tokens", None) | |
| if emb is None or getattr(emb, "weight", None) is not None: | |
| return | |
| if not hasattr(emb, "weight_packed"): | |
| return | |
| from compressed_tensors.compressors import BaseCompressor | |
| compressor = BaseCompressor.get_value_from_registry("pack-quantized") | |
| if isinstance(compressor, type): | |
| compressor = compressor() | |
| compressor.decompress_module(emb) | |
| # Снять с модуля признак квантованного. Иначе библиотека увидит его в | |
| # своём списке на первом прямом проходе (`ct_decompress_hook` | |
| # разворачивает всю модель разом) и упадёт с `KeyError: weight_packed`, | |
| # потому что упакованного тензора здесь уже нет. Замерено 26.07.2026. | |
| # | |
| # Именно удалить атрибут, а не обнулить: `is_module_quantized` сперва | |
| # проверяет его наличие, а затем читает поле `weights`, поэтому | |
| # присвоенный None даёт `AttributeError: 'NoneType' object has no | |
| # attribute 'weights'`. Тоже замерено. | |
| if hasattr(emb, "quantization_scheme"): | |
| del emb.quantization_scheme | |
| # ---- проход с оверлеем ------------------------------------------------- | |
| def forward(self, input_ids=None, attention_mask=None, position_ids=None, | |
| past_key_values=None, inputs_embeds=None, use_cache=None, | |
| **kwargs): | |
| if inputs_embeds is None: | |
| inputs_embeds = self.embed_tokens(input_ids) | |
| text_position_ids = position_ids | |
| if text_position_ids is not None and text_position_ids.ndim == 3: | |
| text_position_ids = text_position_ids[0] | |
| if text_position_ids is None: | |
| seen = past_key_values.get_seq_length() if past_key_values is not None else 0 | |
| text_position_ids = torch.arange( | |
| seen, seen + inputs_embeds.shape[1], device=inputs_embeds.device | |
| ).unsqueeze(0) | |
| mask_kwargs = {"config": self.config, "input_embeds": inputs_embeds, | |
| "attention_mask": attention_mask, | |
| "past_key_values": past_key_values, | |
| "position_ids": text_position_ids} | |
| try: | |
| masks = {"full_attention": create_causal_mask(**mask_kwargs), | |
| "linear_attention": create_recurrent_attention_mask(**mask_kwargs)} | |
| except TypeError: # старая сигнатура: inputs_embeds вместо input_embeds | |
| mask_kwargs["inputs_embeds"] = mask_kwargs.pop("input_embeds") | |
| masks = {"full_attention": create_causal_mask(**mask_kwargs), | |
| "linear_attention": create_recurrent_attention_mask(**mask_kwargs)} | |
| hidden_states = inputs_embeds | |
| position_embeddings = self.rotary_emb(hidden_states, position_ids) | |
| replay_cache = self._replay_cache(past_key_values) | |
| for i, layer in enumerate(self.layers[: self.config.num_hidden_layers]): | |
| hidden_states = layer( | |
| hidden_states, position_embeddings=position_embeddings, | |
| attention_mask=masks[self.config.layer_types[i]], | |
| position_ids=text_position_ids, past_key_values=past_key_values, | |
| use_cache=use_cache, **kwargs) | |
| name = ("sb_a" if i == SUPERBLOCK_A_ATTENTION | |
| else "sb_b" if i == SUPERBLOCK_B_ATTENTION else None) | |
| if name is not None: | |
| hidden_states = self.altay_overlay.run( | |
| name, hidden_states, | |
| recurrent_mask=masks["linear_attention"], | |
| attention_mask=masks["full_attention"], | |
| position_embeddings=position_embeddings, | |
| position_ids=text_position_ids, | |
| replay_cache=replay_cache, **kwargs) | |
| hidden_states = self.norm(hidden_states) | |
| return Qwen3_5ModelOutputWithPast(last_hidden_state=hidden_states, | |
| past_key_values=past_key_values) | |
| def _replay_cache(self, past_key_values): | |
| """Отдельный кэш для повторов: веса общие, рекуррентные состояния — нет.""" | |
| if past_key_values is None: | |
| return None | |
| cache = getattr(self, "_altay_replay_cache", None) | |
| if cache is None or type(cache) is not type(past_key_values): | |
| cache = type(past_key_values)(config=self.config) | |
| object.__setattr__(self, "_altay_replay_cache", cache) | |
| return cache | |
| class LomonosovZenitAltayModel(Qwen3_5Model): | |
| """Мультимодальная обёртка: зрение стоковое, текст с оверлеем.""" | |
| def __init__(self, config): | |
| super().__init__(config) | |
| self.language_model = LomonosovZenitAltayTextModel(config.text_config) | |
| self.post_init() | |
| class LomonosovZenitAltayForConditionalGeneration(Qwen3_5ForConditionalGeneration): | |
| """Точка входа для `from_pretrained`. | |
| Класс мультимодальный, и это существенно: чекпоинт держит веса под | |
| `model.language_model.*`, а `AutoModelForCausalLM` строит текстовое дерево | |
| под `model.*`. Замерено, что при этом совпадает ноль ключей из 853 — мимо | |
| идут даже нормировки, которых квантование не касается. Правильный автокласс | |
| здесь `AutoModelForImageTextToText`; он и прописан в `auto_map`. | |
| """ | |
| def __init__(self, config): | |
| super().__init__(config) | |
| self.model = LomonosovZenitAltayModel(config) | |
| self.post_init() | |
| def from_pretrained(cls, *args, **kwargs): | |
| # Распаковка эмбеддингов возможна только после того, как веса легли на | |
| # место: до загрузки распаковывать нечего. | |
| model = super().from_pretrained(*args, **kwargs) | |
| text = getattr(getattr(model, "model", None), "language_model", None) | |
| if hasattr(text, "materialize_packed_embedding"): | |
| text.materialize_packed_embedding() | |
| return model | |
| __all__ = [ | |
| "LomonosovZenitAltayForConditionalGeneration", | |
| "LomonosovZenitAltayModel", | |
| "LomonosovZenitAltayTextModel", | |
| "AltayOverlay", | |
| ] | |