import spaces # MUST come before any torch/CUDA-touching import import torch import gradio as gr from transformers import AutoModelForImageTextToText, AutoTokenizer MODEL_ID = "17slever17/translate-gemma-4-sub-e4b" tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) model = AutoModelForImageTextToText.from_pretrained( MODEL_ID, torch_dtype=torch.bfloat16, ).to("cuda") model.eval() SUPPORTED_LANGUAGES = [ "English", "Russian", "Spanish", "German", "Japanese", "French", "Portuguese", "Chinese", "Dutch", "Italian", "Korean", ] STYLES = ["neutral", "friendly", "official"] def _build_system_message(source_lang: str, target_lang: str, rules: str, style: str) -> str: """Build the system message exactly as specified in the model card.""" parts = [f"TASK: Translate {source_lang} subtitles into {target_lang}."] if rules.strip(): parts.append(f"RULES: {rules.strip()}") parts.append(f"STYLE: {style}.") parts.append( "Translate only CURRENT_SOURCE. PREVIOUS_SOURCE and PREVIOUS_TRANSLATION are context only. " "Preserve meaning, tone, slang, profanity, uncertainty, repetitions and incomplete speech. " "Return only the final translation without labels or commentary." ) return "\n".join(parts) def _build_user_message(prev_source: str, prev_translation: str, current_source: str) -> str: """Build the user message following the model card's format.""" blocks = [] if prev_source.strip(): blocks.append(f"[PREVIOUS_SOURCE]\n{prev_source.strip()}") if prev_translation.strip(): blocks.append(f"[PREVIOUS_TRANSLATION]\n{prev_translation.strip()}") blocks.append(f"[CURRENT_SOURCE]\n{current_source.strip()}") return "\n\n".join(blocks) @spaces.GPU(duration=60) def translate( current_source: str, source_language: str = "English", target_language: str = "Russian", previous_source: str = "", previous_translation: str = "", rules: str = "", style: str = "friendly", ) -> str: """Translate a subtitle line using context-aware Gemma 4 Sub model. Args: current_source: The subtitle segment to translate. source_language: Language of the source text. target_language: Language to translate into. previous_source: Previous source-language subtitles for context (optional). previous_translation: Previous translated subtitles for context (optional). rules: Additional rules (speaker name, gender, terminology, etc.) (optional). style: Translation style — neutral, friendly, or official. """ if not current_source.strip(): return "Please enter a subtitle line to translate." system_msg = _build_system_message(source_language, target_language, rules, style) user_msg = _build_user_message(previous_source, previous_translation, current_source) messages = [ {"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, return_tensors="pt", return_dict=True, ).to("cuda") with torch.inference_mode(): output = model.generate( **inputs, max_new_tokens=256, do_sample=False, ) generated_tokens = output[0, inputs["input_ids"].shape[1]:] translation = tokenizer.decode(generated_tokens, skip_special_tokens=True).strip() return translation CSS = """ #col-container { max-width: 900px; margin: 0 auto; } .dark .gradio-container { color: var(--body-text-color); } """ with gr.Blocks() as demo: gr.Markdown( "# 🌐 Translate Gemma 4 Sub — Context-Aware Subtitle Translation\n" "A multilingual Gemma 4 fine-tune specialized for subtitle translation. " "Provide previous source and translation context for natural, coherent translations." ) with gr.Column(elem_id="col-container"): with gr.Row(): source_language = gr.Dropdown( label="Source Language", choices=SUPPORTED_LANGUAGES, value="English", ) target_language = gr.Dropdown( label="Target Language", choices=SUPPORTED_LANGUAGES, value="Russian", ) style = gr.Dropdown( label="Style", choices=STYLES, value="friendly", ) current_source = gr.Textbox( label="Current Source (to translate)", placeholder="Yeah, well... I changed my mind.", lines=2, ) with gr.Accordion("Context & Rules (optional)", open=False): previous_source = gr.Textbox( label="Previous Source (context only)", placeholder="I thought you said you weren't coming.", lines=2, ) previous_translation = gr.Textbox( label="Previous Translation (context only)", placeholder="Я думала, ты сказала, что не придёшь.", lines=2, ) rules = gr.Textbox( label="Rules (speaker name, gender, terminology, etc.)", placeholder="speaker gender: female", lines=2, ) run_btn = gr.Button("Translate", variant="primary") output = gr.Textbox( label="Translation", lines=3, interactive=False, ) run_btn.click( fn=translate, inputs=[ current_source, source_language, target_language, previous_source, previous_translation, rules, style, ], outputs=output, api_name="translate", ) gr.Examples( examples=[ ["Yeah, well... I changed my mind.", "English", "Russian", "friendly"], ["I thought you said you weren't coming.", "English", "Spanish", "neutral"], ["Wait, hold on a second...", "English", "Japanese", "friendly"], ["That's not what I meant at all.", "English", "German", "official"], ["Could you repeat that, please?", "English", "French", "official"], ], inputs=[current_source, source_language, target_language, style], outputs=output, fn=translate, cache_examples=True, cache_mode="lazy", ) if __name__ == "__main__": demo.launch(theme=gr.themes.Citrus(), css=CSS, mcp_server=True)