#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright (c) 2026 Hamid Wakili """Run offline generation from the released Safetensors checkpoint.""" from __future__ import annotations import argparse import json from pathlib import Path from typing import Sequence import torch import torch.nn.functional as F from safetensors.torch import load_file from bpe_tokenizer import BPE_Tokenizer from german_transformer_model import GermanGPT, GermanGPTConfig def load_runtime( model_dir: str | Path, device_name: str = "auto", ) -> tuple[BPE_Tokenizer, GermanGPT, torch.device]: """Load the tokenizer, configuration, and Safetensors weights.""" root = Path(model_dir) device = torch.device( "cuda" if device_name == "auto" and torch.cuda.is_available() else "cpu" if device_name == "auto" else device_name ) with (root / "config.json").open("r", encoding="utf-8") as file: config = GermanGPTConfig.from_dict(json.load(file)) tokenizer = BPE_Tokenizer.load(root / "tokenizer" / "de_bpe_32k") if tokenizer.vocabulary_size() != config.vocab_size: raise ValueError( f"Tokenizer size {tokenizer.vocabulary_size()} does not match " f"model vocab_size {config.vocab_size}" ) model = GermanGPT(config) state_dict = load_file(root / "model.safetensors", device="cpu") model.load_state_dict(state_dict, strict=True) model.to(device).eval() return tokenizer, model, device def filter_logits(logits: torch.Tensor, top_k: int, top_p: float) -> torch.Tensor: """Apply top-k and nucleus filtering to one vocabulary-sized logit vector.""" filtered = logits.clone() if top_k > 0: cutoff = torch.topk(filtered, min(top_k, filtered.numel())).values[-1] filtered[filtered < cutoff] = float("-inf") if top_p < 1.0: sorted_logits, sorted_indices = torch.sort(filtered, descending=True) probabilities = F.softmax(sorted_logits, dim=-1) cumulative = torch.cumsum(probabilities, dim=-1) remove = cumulative > top_p remove[1:] = remove[:-1].clone() remove[0] = False filtered[sorted_indices[remove]] = float("-inf") return filtered def generate( prompt: str, tokenizer: BPE_Tokenizer, model: GermanGPT, device: torch.device, max_new_tokens: int, temperature: float, top_k: int, top_p: float, repetition_penalty: float, ) -> str: """Generate a completion with greedy or temperature-based sampling.""" token_ids = tokenizer.encode(prompt) if not token_ids: token_ids = [tokenizer.special_tokens.get("", 2)] generated = list(token_ids) eos_id = tokenizer.special_tokens.get("", 3) with torch.inference_mode(): for _ in range(max_new_tokens): context = generated[-model.config.context_len :] input_ids = torch.tensor([context], dtype=torch.long, device=device) logits, _ = model(input_ids) next_logits = logits[0, -1].float() if repetition_penalty != 1.0: for token_id in set(generated): value = next_logits[token_id] next_logits[token_id] = ( value * repetition_penalty if value < 0 else value / repetition_penalty ) if temperature <= 0: next_id = int(torch.argmax(next_logits).item()) else: next_logits = filter_logits( next_logits / temperature, top_k=top_k, top_p=top_p, ) probabilities = F.softmax(next_logits, dim=-1) next_id = int(torch.multinomial(probabilities, 1).item()) generated.append(next_id) if next_id == eos_id: break return tokenizer.decode(generated) def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: """Parse command-line arguments.""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--model-dir", default=".") parser.add_argument("--prompt", required=True) parser.add_argument("--device", default="auto") parser.add_argument("--max-new-tokens", type=int, default=80) parser.add_argument("--temperature", type=float, default=0.8) parser.add_argument("--top-k", type=int, default=40) parser.add_argument("--top-p", type=float, default=0.9) parser.add_argument("--repetition-penalty", type=float, default=1.1) return parser.parse_args(argv) def main(argv: Sequence[str] | None = None) -> int: """Load the release and print one generated completion.""" args = parse_args(argv) if args.max_new_tokens < 0: raise ValueError("--max-new-tokens must be non-negative") if args.temperature < 0: raise ValueError("--temperature must be non-negative") if args.top_k < 0: raise ValueError("--top-k must be non-negative") if not 0 < args.top_p <= 1: raise ValueError("--top-p must be in (0, 1]") if args.repetition_penalty <= 0: raise ValueError("--repetition-penalty must be positive") tokenizer, model, device = load_runtime(args.model_dir, args.device) print( generate( prompt=args.prompt, tokenizer=tokenizer, model=model, device=device, max_new_tokens=args.max_new_tokens, temperature=args.temperature, top_k=args.top_k, top_p=args.top_p, repetition_penalty=args.repetition_penalty, ) ) return 0 if __name__ == "__main__": raise SystemExit(main())