MiniTransformer-91M / inference /generate_stream.py
Vivid86's picture
Upload folder using huggingface_hub
9e30969 verified
Raw History Blame Contribute Delete
2.77 kB
"""
inference/generate_stream.py — Real-time streaming generation for MiniTransformer.
Yields incremental text tokens as they are sampled autoregressively,
enabling responsive ChatGPT-style typewriter streaming in web interfaces.
"""
from typing import Generator
import torch
import torch.nn.functional as F
from tokenizer.bpe_tokenizer import BPETokenizer
from inference.generate import sample_logits
def generate_stream(
model,
prompt: str,
tokenizer: "BPETokenizer | None" = None,
max_new_tokens: int = 100,
temperature: float = 0.6,
top_k: int = 40,
top_p: float = 0.9,
repetition_penalty: float = 1.15,
penalty_window: int = 64,
device: str = "cuda" if torch.cuda.is_available() else "cpu",
) -> Generator[str, None, None]:
"""
Generate text from a prompt, yielding the updated assistant response after each token.
Yields:
Full decoded assistant response text accumulated so far.
"""
if tokenizer is None:
tokenizer = BPETokenizer("dataProcessed/tokenizer.json")
context_len = model.config.context_len
model.eval()
model.to(device)
# Encode prompt
prompt_ids = tokenizer.encode(prompt)
tokens = torch.tensor(prompt_ids, dtype=torch.long, device=device).unsqueeze(0)
prompt_len = len(prompt_ids)
past_kvs = None
temperature = max(temperature, 1e-5)
generated_ids = []
with torch.no_grad():
for _ in range(max_new_tokens):
if past_kvs is not None:
context = tokens[:, -1:]
else:
context = tokens[:, -context_len:]
logits, past_kvs_new = model(context, past_kvs)
past_kvs = past_kvs_new
next_logits = logits[:, -1, :].clone()
# Repetition penalty
if repetition_penalty > 1.0 and tokens.shape[1] > 0:
recent = tokens[0, -penalty_window:].tolist()
for tid in set(recent):
if next_logits[0, tid] > 0:
next_logits[0, tid] /= repetition_penalty
else:
next_logits[0, tid] *= repetition_penalty
next_id = sample_logits(next_logits, temperature, top_k, top_p)
if next_id == tokenizer.eos_id:
break
generated_ids.append(next_id)
next_tensor = torch.tensor([[next_id]], dtype=torch.long, device=device)
tokens = torch.cat([tokens, next_tensor], dim=1)
# Robust BPE byte-level decoding
full_decoded = tokenizer.decode(generated_ids)
clean_text = tokenizer.clean(full_decoded)
yield clean_text