Text Generation
Transformers
Safetensors
sumi
mask-generation
diffusion
discrete-diffusion
uniform-diffusion
custom_code
Instructions to use tohoku-nlp/sumi-7b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use tohoku-nlp/sumi-7b with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="tohoku-nlp/sumi-7b", trust_remote_code=True)# Load model directly from transformers import AutoModelForMaskGeneration model = AutoModelForMaskGeneration.from_pretrained("tohoku-nlp/sumi-7b", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use tohoku-nlp/sumi-7b with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "tohoku-nlp/sumi-7b" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "tohoku-nlp/sumi-7b", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/tohoku-nlp/sumi-7b
- SGLang
How to use tohoku-nlp/sumi-7b 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 "tohoku-nlp/sumi-7b" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "tohoku-nlp/sumi-7b", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'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 "tohoku-nlp/sumi-7b" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "tohoku-nlp/sumi-7b", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use tohoku-nlp/sumi-7b with Docker Model Runner:
docker model run hf.co/tohoku-nlp/sumi-7b
Fix generate(): full 2048 canvas + EOSBOS anchor + trim_at_eos for coherent generation
Browse filesgenerate() now denoises a full canvas_length (default 2048) canvas instead of prompt+max_new_tokens, anchors the <|endoftext|><|beginoftext|> delimiter at prompt+max_new_tokens (max_new_tokens is the content budget), and trims output at the first EOS (trim_at_eos). Full untrimmed canvas returned as out.canvas. New params canvas_length/anchor_eosbos/trim_at_eos all resolve via generation_config.
- generation_sumi.py +57 -8
generation_sumi.py
CHANGED
|
@@ -50,6 +50,7 @@ class SumiGenerationOutput(ModelOutput):
|
|
| 50 |
past_key_values: Cache | None = None
|
| 51 |
logits: None = None
|
| 52 |
hidden_states: None = None
|
|
|
|
| 53 |
|
| 54 |
|
| 55 |
class SumiGenerationConfig(GenerationConfig):
|
|
@@ -333,6 +334,32 @@ class SumiGenerationMixin(GenerationMixin):
|
|
| 333 |
return resolved
|
| 334 |
return default
|
| 335 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
@torch.no_grad()
|
| 337 |
def generate(
|
| 338 |
self,
|
|
@@ -351,6 +378,9 @@ class SumiGenerationMixin(GenerationMixin):
|
|
| 351 |
tokens_per_step: Optional[int] = None,
|
| 352 |
frozen: Optional[List] = None,
|
| 353 |
denoise_end: Optional[List[int]] = None,
|
|
|
|
|
|
|
|
|
|
| 354 |
progress_callback: Optional[Callable] = None,
|
| 355 |
**kwargs,
|
| 356 |
) -> SumiGenerationOutput:
|
|
@@ -384,6 +414,9 @@ class SumiGenerationMixin(GenerationMixin):
|
|
| 384 |
tokens_per_step = self._resolve(
|
| 385 |
tokens_per_step, "tokens_per_step", 1, generation_config, self_generation_config
|
| 386 |
)
|
|
|
|
|
|
|
|
|
|
| 387 |
|
| 388 |
device = self.model.embed_tokens.weight.device
|
| 389 |
if input_ids is None:
|
|
@@ -422,14 +455,29 @@ class SumiGenerationMixin(GenerationMixin):
|
|
| 422 |
if max_new_tokens < 0:
|
| 423 |
raise ValueError("max_new_tokens must be non-negative.")
|
| 424 |
if max_new_tokens == 0:
|
| 425 |
-
return SumiGenerationOutput(sequences=input_ids)
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 429 |
raise ValueError(
|
| 430 |
-
f"
|
| 431 |
-
f"({self.config.max_position_embeddings})."
|
| 432 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
|
| 434 |
if attention_mask is None:
|
| 435 |
attention_mask = torch.ones_like(input_ids)
|
|
@@ -449,7 +497,7 @@ class SumiGenerationMixin(GenerationMixin):
|
|
| 449 |
generator = torch.Generator()
|
| 450 |
generator.manual_seed(seed)
|
| 451 |
|
| 452 |
-
completion_shape = (batch_size,
|
| 453 |
try:
|
| 454 |
completion_ids = torch.randint(
|
| 455 |
low=0,
|
|
@@ -540,7 +588,8 @@ class SumiGenerationMixin(GenerationMixin):
|
|
| 540 |
if was_training:
|
| 541 |
self.train()
|
| 542 |
|
| 543 |
-
|
|
|
|
| 544 |
|
| 545 |
|
| 546 |
__all__ = [
|
|
|
|
| 50 |
past_key_values: Cache | None = None
|
| 51 |
logits: None = None
|
| 52 |
hidden_states: None = None
|
| 53 |
+
canvas: torch.LongTensor | None = None # full untrimmed denoised canvas
|
| 54 |
|
| 55 |
|
| 56 |
class SumiGenerationConfig(GenerationConfig):
|
|
|
|
| 334 |
return resolved
|
| 335 |
return default
|
| 336 |
|
| 337 |
+
def _trim_at_eos(self, generated_ids, prompt_length):
|
| 338 |
+
"""Cut each row at the first EOS in its generated region (dropping the EOS, the
|
| 339 |
+
anchored BOS delimiter, and the denoised tail), then right-pad the batch back into
|
| 340 |
+
a rectangular tensor. Prompt tokens are kept. Mirrors the eval harness's
|
| 341 |
+
``_extract_text`` (`gen_ids[:gen_ids.index(eos)]`). The pad filler is a special
|
| 342 |
+
token, so ``decode(..., skip_special_tokens=True)`` yields clean text."""
|
| 343 |
+
eos_id = self.config.eos_token_id
|
| 344 |
+
if eos_id is None:
|
| 345 |
+
return generated_ids
|
| 346 |
+
pad_id = self.config.pad_token_id
|
| 347 |
+
pad_id = eos_id if pad_id is None else pad_id
|
| 348 |
+
batch_size = generated_ids.shape[0]
|
| 349 |
+
rows, max_len = [], prompt_length
|
| 350 |
+
for i in range(batch_size):
|
| 351 |
+
row = generated_ids[i]
|
| 352 |
+
hit = (row[prompt_length:] == eos_id).nonzero(as_tuple=True)[0]
|
| 353 |
+
cut = prompt_length + int(hit[0].item()) if hit.numel() > 0 else row.shape[0]
|
| 354 |
+
rows.append(row[:cut])
|
| 355 |
+
max_len = max(max_len, cut)
|
| 356 |
+
out = torch.full((batch_size, max(max_len, 1)), pad_id,
|
| 357 |
+
dtype=generated_ids.dtype, device=generated_ids.device)
|
| 358 |
+
for i, row in enumerate(rows):
|
| 359 |
+
if row.shape[0] > 0:
|
| 360 |
+
out[i, : row.shape[0]] = row
|
| 361 |
+
return out
|
| 362 |
+
|
| 363 |
@torch.no_grad()
|
| 364 |
def generate(
|
| 365 |
self,
|
|
|
|
| 378 |
tokens_per_step: Optional[int] = None,
|
| 379 |
frozen: Optional[List] = None,
|
| 380 |
denoise_end: Optional[List[int]] = None,
|
| 381 |
+
canvas_length: Optional[int] = None,
|
| 382 |
+
anchor_eosbos: Optional[bool] = None,
|
| 383 |
+
trim_at_eos: Optional[bool] = None,
|
| 384 |
progress_callback: Optional[Callable] = None,
|
| 385 |
**kwargs,
|
| 386 |
) -> SumiGenerationOutput:
|
|
|
|
| 414 |
tokens_per_step = self._resolve(
|
| 415 |
tokens_per_step, "tokens_per_step", 1, generation_config, self_generation_config
|
| 416 |
)
|
| 417 |
+
canvas_length = self._resolve(canvas_length, "canvas_length", 2048, generation_config, self_generation_config)
|
| 418 |
+
anchor_eosbos = self._resolve(anchor_eosbos, "anchor_eosbos", True, generation_config, self_generation_config)
|
| 419 |
+
trim_at_eos = self._resolve(trim_at_eos, "trim_at_eos", True, generation_config, self_generation_config)
|
| 420 |
|
| 421 |
device = self.model.embed_tokens.weight.device
|
| 422 |
if input_ids is None:
|
|
|
|
| 455 |
if max_new_tokens < 0:
|
| 456 |
raise ValueError("max_new_tokens must be non-negative.")
|
| 457 |
if max_new_tokens == 0:
|
| 458 |
+
return SumiGenerationOutput(sequences=input_ids, canvas=input_ids)
|
| 459 |
+
|
| 460 |
+
# The model is trained on a packed, fixed-length canvas, so generation runs on a
|
| 461 |
+
# full `canvas_length` canvas (default 2048) rather than just prompt+max_new_tokens.
|
| 462 |
+
# `max_new_tokens` is the content budget: the EOS,BOS document delimiter is anchored
|
| 463 |
+
# at prompt_length+max_new_tokens, the rest of the canvas is denoised as context, and
|
| 464 |
+
# decoding is cut at the first EOS (trim_at_eos, default True).
|
| 465 |
+
ceiling = self.config.max_position_embeddings
|
| 466 |
+
canvas_length = min(int(canvas_length), ceiling)
|
| 467 |
+
reserve = 2 if anchor_eosbos else 0
|
| 468 |
+
if prompt_length + reserve >= canvas_length:
|
| 469 |
raise ValueError(
|
| 470 |
+
f"prompt_length ({prompt_length}) leaves no room in canvas_length ({canvas_length})."
|
|
|
|
| 471 |
)
|
| 472 |
+
budget = max(1, min(max_new_tokens, canvas_length - prompt_length - reserve))
|
| 473 |
+
total_length = canvas_length
|
| 474 |
+
completion_length = total_length - prompt_length
|
| 475 |
+
|
| 476 |
+
# Anchor the EOS,BOS delimiter at the end of the content budget. The caller can
|
| 477 |
+
# override by passing an explicit `frozen` set (then it takes precedence).
|
| 478 |
+
if anchor_eosbos and frozen is None:
|
| 479 |
+
eos_pos = prompt_length + budget
|
| 480 |
+
frozen = [(eos_pos, self.config.eos_token_id), (eos_pos + 1, self.config.bos_token_id)]
|
| 481 |
|
| 482 |
if attention_mask is None:
|
| 483 |
attention_mask = torch.ones_like(input_ids)
|
|
|
|
| 497 |
generator = torch.Generator()
|
| 498 |
generator.manual_seed(seed)
|
| 499 |
|
| 500 |
+
completion_shape = (batch_size, completion_length)
|
| 501 |
try:
|
| 502 |
completion_ids = torch.randint(
|
| 503 |
low=0,
|
|
|
|
| 588 |
if was_training:
|
| 589 |
self.train()
|
| 590 |
|
| 591 |
+
sequences = self._trim_at_eos(generated_ids, prompt_length) if trim_at_eos else generated_ids
|
| 592 |
+
return SumiGenerationOutput(sequences=sequences, canvas=generated_ids)
|
| 593 |
|
| 594 |
|
| 595 |
__all__ = [
|