import os import requests import gradio as gr import re # Registry for Key Status key_status_registry = {} class AIService: @staticmethod def get_all_keys(prefix): # សម្រួលមកប្រើ Key តែមួយចំៗសម្រាប់ Gemini និងម៉ូដែលផ្សេងទៀត # វានឹងឆែករកមើលឈ្មោះដូចជា GEMINI_API_KEY ឬ GROQ_API_KEY ផ្ទាល់តែម្ដង env_name = prefix.rstrip('_') # ដកសញ្ញា _ ចេញដើម្បីឱ្យត្រូវនឹងឈ្មោះ Standard key = os.environ.get(env_name) if key: return [key] return [] @staticmethod def get_status_html(engine, target_lang=None): if "Gemini" in engine: prefix = "GEMINI_API_KEY" elif "Llama" in engine: prefix = "GROQ_API_KEY" else: prefix = "SEA_LION_API_KEY" keys = AIService.get_all_keys(prefix) label = engine.split(" ")[-1] status_label = "ស្ថានភាព/Status" html = f"
{label} {status_label}:" if not keys: html += "រកមិនឃើញ Key" else: for k in keys: state = key_status_registry.get(k, "ready") color = "#22c55e" if state == "ready" else "#ef4444" html += f"
" return html + "
" @staticmethod def call_api(engine, prompt): temp = 0.8 if "Gemini" in engine: keys = AIService.get_all_keys("GEMINI_API_KEY") for key in keys: try: # ប្រើម៉ូដែល Gemini 2.0 Flash ផ្លូវការ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={key}" res = requests.post(url, json={ "contents": [{"parts": [{"text": prompt}]}], "generationConfig": {"temperature": temp}, "safetySettings": [ {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"}, {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"}, {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"}, {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"} ] }, timeout=45) if res.status_code == 200: key_status_registry[key] = "ready" return res.json()['candidates'][0]['content']['parts'][0]['text'].strip() key_status_registry[key] = "dead" except: continue elif "Llama" in engine: keys = AIService.get_all_keys("GROQ_API_KEY") for key in keys: try: res = requests.post("https://api.groq.com/openai/v1/chat/completions", headers={"Authorization": f"Bearer {key}"}, json={"model": "llama-3.3-70b-versatile", "messages": [{"role": "user", "content": prompt}], "temperature": temp}, timeout=30) if res.status_code == 200: key_status_registry[key] = "ready" return res.json()['choices'][0]['message']['content'].strip() key_status_registry[key] = "dead" except: continue else: key = os.environ.get("SEA_LION_API_KEY") try: res = requests.post("https://api.sea-lion.ai/v1/chat/completions", headers={"Authorization": f"Bearer {key}"}, json={ "model": "aisingapore/Gemma-SEA-LION-v4-27B-IT", "messages": [{"role": "system", "content": "You are an expert translator. Output ONLY translation."}, {"role": "user", "content": prompt}], "temperature": 0.7 }, timeout=60) if res.status_code == 200: key_status_registry[key] = "ready" return res.json()['choices'][0]['message']['content'].strip() key_status_registry[key] = "dead" except: pass return None def translator_hub(text, target_lang, engine): if not text.strip(): return "", AIService.get_status_html(engine) lang_name = re.sub(r'[^\w\s]', '', target_lang).strip() instruction = f"""Translate to {lang_name} STRICTLY: 1. OUTPUT ONLY translation. No notes. 2. SLANG & IDIOMS: Adapt naturally. 3. CONTEXT: Use proper pronouns (បង, អូន, អញ, ឯង) based on mood. 4. SRT FORMAT: Preserve timing/numbering. 5. NO MIXED LANGUAGES: No English words in output.""" prompt = f"{instruction}\n\nCONTENT:\n{text}" result = AIService.call_api(engine, prompt) if result: cleaned = re.sub(r'```[a-zA-Z]*\n?|```', '', result).strip() return cleaned, AIService.get_status_html(engine) return "❌ Error: មិនអាចទាក់ទងទៅ API បានទេ (ពិនិត្យ Key របស់មេ)", AIService.get_status_html(engine) # CSS configuration custom_css = """ body { background: #0f172a !important; } .gr-markdown h1 { background: linear-gradient(90deg, #60a5fa, #f472b6); -webkit-background-clip: text; color: transparent !important; } .btn-trans { background: #10b981 !important; color: white !important; } """ with gr.Blocks(title="SRT Pro") as demo: gr.Markdown("

🎬 SMART TRANSLATOR PRO

") with gr.Row(): with gr.Column(): lang_opt = gr.Dropdown(["🇰🇭 Khmer", "🇺🇸 English", "🇨🇳 Chinese", "🇹🇭 Thai"], value="🇰🇭 Khmer", label="Language") engine_opt = gr.Radio(["💎 Gemini", "🦙 Llama", "🦁 SEA-LION"], value="💎 Gemini", label="Model") status_ui = gr.HTML(AIService.get_status_html("💎 Gemini")) input_box = gr.Textbox(label="Original Content", lines=10, placeholder="ដាក់អត្ថបទនៅទីនេះ...") btn_trans = gr.Button("⚡ Translate", variant="primary", elem_classes="btn-trans") with gr.Column(): output_box = gr.Textbox(label="Result", lines=20, interactive=False) btn_copy = gr.Button("📋 Copy Result") engine_opt.change(AIService.get_status_html, inputs=[engine_opt], outputs=[status_ui]) btn_trans.click(translator_hub, [input_box, lang_opt, engine_opt], [output_box, status_ui]) btn_copy.click(None, output_box, js="(v) => { navigator.clipboard.writeText(v); alert('✅ ចម្លងរួចរាល់!'); }") if __name__ == "__main__": # បញ្ចូល CSS ក្នុង launch() ដើម្បីជួសជុល Gradio 6.0 Warning demo.launch(server_name="0.0.0.0", server_port=7860, css=custom_css, ssr_mode=False)