| """Gradio demo for the New Prussian translator (Apertus-8B-int8 + LoRA). |
| |
| Loads the pre-quantized int8 base model `strfry/Apertus-8B-Instruct-2509-int8` |
| and applies the LoRA adapter `strfry/apertus-8b-prussian-youtube` — all at |
| module level on CPU. ZeroGPU transfers everything to VRAM automatically when |
| a request enters @spaces.GPU. No lazy loading, no safetensors CUDA issues. |
| """ |
|
|
| import json |
| import torch |
| import gradio as gr |
| import safetensors.torch |
| from huggingface_hub import hf_hub_download |
| from transformers import AutoModelForCausalLM, AutoTokenizer |
| from peft import PeftModel, LoraConfig, set_peft_model_state_dict |
|
|
| |
| try: |
| import spaces |
|
|
| GPU = spaces.GPU |
| except ImportError: |
|
|
| def GPU(func=None, **_kwargs): |
| if func is None: |
| return lambda f: f |
| return func |
|
|
|
|
| BASE_MODEL = "strfry/Apertus-8B-Instruct-2509-int8" |
| ADAPTER = "strfry/apertus-8b-prussian-youtube" |
|
|
| SYSTEM_PROMPT = "Translate to reconstructed neo-prussian:" |
| MAX_NEW_TOKENS = 100 |
|
|
| |
| |
| _tokenizer = AutoTokenizer.from_pretrained(ADAPTER, trust_remote_code=True) |
|
|
| |
| |
| |
| _base = AutoModelForCausalLM.from_pretrained( |
| BASE_MODEL, |
| device_map="auto", |
| trust_remote_code=True, |
| ) |
|
|
| |
| |
| |
| |
| _config_path = hf_hub_download(ADAPTER, "adapter_config.json") |
| with open(_config_path) as f: |
| _peft_config = LoraConfig(**json.load(f)) |
|
|
| _model = PeftModel(_base, _peft_config) |
|
|
| _weights_path = hf_hub_download(ADAPTER, "adapter_model.safetensors") |
| _adapter_weights = safetensors.torch.load_file(_weights_path, device="cpu") |
|
|
| |
| |
| |
| |
| |
| _load_result = set_peft_model_state_dict(_model, _adapter_weights) |
| assert not _load_result.unexpected_keys, ( |
| f"Adapter weights did not load: {_load_result.unexpected_keys[:5]}" |
| ) |
| _model.eval() |
|
|
|
|
| @GPU(duration=20) |
| def translate(text: str) -> str: |
| """Tokenize, generate, decode — the GPU-heavy work lives here.""" |
| if not text.strip(): |
| return "" |
|
|
| messages = [ |
| {"role": "system", "content": SYSTEM_PROMPT}, |
| {"role": "user", "content": text.strip()}, |
| ] |
| inputs = ( |
| _tokenizer.apply_chat_template( |
| messages, |
| add_generation_prompt=True, |
| return_tensors="pt", |
| return_dict=True, |
| ) |
| .to(_base.device) |
| ) |
|
|
| |
| |
| |
| |
| with torch.no_grad(): |
| out = _model.generate( |
| **inputs, |
| max_new_tokens=MAX_NEW_TOKENS, |
| do_sample=False, |
| repetition_penalty=1.2, |
| pad_token_id=_tokenizer.eos_token_id, |
| ) |
| new_tokens = out[0][inputs["input_ids"].shape[1] :] |
| result = _tokenizer.decode(new_tokens, skip_special_tokens=False).strip() |
| result = result.replace("<|im_end|>", "").strip() |
| cutoff = result.find("<|im_start|>") |
| if cutoff != -1: |
| result = result[:cutoff].strip() |
| return result |
|
|
|
|
| with gr.Blocks(title="New Prussian Translator") as demo: |
| gr.Markdown( |
| "# New Prussian Translator\n" |
| "Apertus-8B + LoRA. Translates **into** reconstructed neo-Prussian " |
| "from German, English, Lithuanian, Latvian, … Model is pre-loaded — " |
| "queries complete in a few seconds." |
| ) |
| gr.Markdown(f"**Fixed system prompt:** `{SYSTEM_PROMPT}`") |
| with gr.Row(): |
| with gr.Column(): |
| text = gr.Textbox(lines=4, label="Source text") |
| btn = gr.Button("Translate", variant="primary") |
| with gr.Column(): |
| output = gr.Textbox(lines=4, label="New Prussian") |
|
|
| gr.Examples( |
| examples=[ |
| ["Ich gehe in den Wald"], |
| ["All is very white."], |
| ["Wie heißt du?"], |
| ], |
| inputs=[text], |
| ) |
|
|
| btn.click(translate, text, output) |
| text.submit(translate, text, output) |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|