takumi0002 commited on
Commit
8dc8ce9
Β·
verified Β·
1 Parent(s): 394eebf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +44 -41
app.py CHANGED
@@ -1,63 +1,75 @@
1
  import os
2
  import requests
3
  import gradio as gr
4
- import time
5
 
6
  # ==========================================
7
- # ៑. αž•αŸ’αž“αŸ‚αž€αž”αž…αŸ’αž…αŸαž€αž‘αŸαžŸ API (αž‡αžΆαž˜αž½αž™ Logic αž”αž‰αŸ’αž‡αžΌαž“αžŸαŸ’αžαžΆαž“αž—αžΆαž– Key)
8
  # ==========================================
9
  class AIService:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  @staticmethod
11
  def get_status_html(engine):
12
- # αž”αž„αŸ’αž€αžΎαž UI αž—αŸ’αž›αžΎαž„αžŸαž‰αŸ’αž‰αžΆ Status αžŸαž˜αŸ’αžšαžΆαž”αŸ‹ Keys αž“αžΈαž˜αž½αž™αŸ—
13
  if "Gemini" in engine:
14
- keys = [os.environ.get(f"GEMINI_API_KEY_{i}") for i in range(1, 6)]
15
- label = "Gemini Keys"
16
  elif "Llama" in engine:
17
- keys = [os.environ.get(f"GROQ_API_KEY_{i}") for i in range(1, 4)]
18
- label = "Llama Keys"
19
  else:
20
- key = os.environ.get("SEA_LION_API_KEY")
21
- keys = [key] if key else []
22
- label = "SEA-LION Key"
23
 
 
24
  html = f"<div style='display: flex; gap: 10px; align-items: center; margin-bottom: 10px;'>"
25
- html += f"<b style='color: #94a3b8; font-size: 12px;'>{label}:</b>"
26
- for i, k in enumerate(keys):
27
- if k:
28
- # αž—αŸ’αž›αžΎαž„αž–αžŽαŸŒαž”αŸƒαžαž„αž›αŸ„αž (Pulse) αž”αž‰αŸ’αž‡αžΆαž€αŸ‹αžαžΆ Key αž˜αžΆαž“αž€αŸ’αž“αž»αž„αž”αŸ’αžšαž–αŸαž“αŸ’αž’
29
- html += f"<div title='Key {i+1} Ready' style='width: 12px; height: 12px; background: #22c55e; border-radius: 50%; box-shadow: 0 0 8px #22c55e; animation: pulse 1.5s infinite;'></div>"
30
- else:
31
- # αž—αŸ’αž›αžΎαž„αž–αžŽαŸŒαž€αŸ’αžšαž αž˜ αž”αž‰αŸ’αž‡αžΆαž€αŸ‹αžαžΆαž’αžαŸ‹αž˜αžΆαž“ Key
32
- html += f"<div title='Key {i+1} Empty' style='width: 12px; height: 12px; background: #ef4444; border-radius: 50%;'></div>"
33
  html += "</div>"
34
  return html
35
 
36
  @staticmethod
37
  def translate_gemini(prompt):
38
- keys = [os.environ.get(f"GEMINI_API_KEY_{i}") for i in range(1, 6) if os.environ.get(f"GEMINI_API_KEY_{i}")]
39
  for key in keys:
40
  try:
41
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent?key={key}"
42
- instruction = "Return ONLY direct translation. Be natural. If asked again, provide an alternative phrasing. Preserve format."
43
  res = requests.post(url, json={"contents": [{"parts": [{"text": f"{instruction}\n\n{prompt}"}]}]}, timeout=15)
44
  if res.status_code == 200:
45
  return res.json()['candidates'][0]['content']['parts'][0]['text'].strip()
46
  except: continue
47
- return "❌ Gemini API Error: Keys might be exhausted."
48
 
49
  @staticmethod
50
  def translate_llama(prompt):
51
- keys = [os.environ.get(f"GROQ_API_KEY_{i}") for i in range(1, 4) if os.environ.get(f"GROQ_API_KEY_{i}")]
52
  for key in keys:
53
  try:
54
  headers = {"Authorization": f"Bearer {key}", "Content-Type": "application/json"}
55
  payload = {
56
  "model": "llama-3.3-70b-versatile",
57
- "messages": [
58
- {"role": "system", "content": "DIRECT TRANSLATION ONLY. Be natural. Provide alternative wording on repeat requests."},
59
- {"role": "user", "content": prompt}
60
- ],
61
  "temperature": 0.7
62
  }
63
  res = requests.post("https://api.groq.com/openai/v1/chat/completions", headers=headers, json=payload, timeout=15)
@@ -75,7 +87,7 @@ class AIService:
75
  headers = {"Authorization": f"Bearer {key}", "Content-Type": "application/json"}
76
  payload = {
77
  "model": "aisingapore/Gemma-SEA-LION-v4-27B-IT",
78
- "messages": [{"role": "user", "content": f"Translate to Khmer (Natural phrasing, return ONLY translation): {prompt}"}],
79
  "temperature": 0.7
80
  }
81
  res = requests.post(url, headers=headers, json=payload, timeout=20)
@@ -87,26 +99,22 @@ class AIService:
87
  def translator_hub(text, target_lang, engine):
88
  if not text.strip(): return ""
89
  lang_name = target_lang.split(" ")[-1]
90
- prompt = f"Target Language: {lang_name}\nText: {text}"
91
-
92
  if "Gemini" in engine: return AIService.translate_gemini(prompt)
93
  elif "Llama" in engine: return AIService.translate_llama(prompt)
94
  else: return AIService.translate_sealion(prompt)
95
 
96
  # ==========================================
97
- # ្. αžŸαŸ’αž‚αŸ’αžšαžΈαž” PWA, CSS αž“αž·αž„ UI Layout αžαŸ’αž˜αžΈ
98
  # ==========================================
99
  pwa_js = """
100
  function initPWA() {
101
  const s = document.createElement('script');
102
  s.src = 'https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js';
103
  document.head.appendChild(s);
104
-
105
- // Pulse animation for API lights
106
  const style = document.createElement('style');
107
  style.innerHTML = `@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }`;
108
  document.head.appendChild(style);
109
-
110
  window.addEventListener('click', (e) => {
111
  if (typeof confetti === 'function') {
112
  confetti({ particleCount: 50, spread: 60, origin: { x: e.clientX / window.innerWidth, y: e.clientY / window.innerHeight } });
@@ -117,7 +125,7 @@ function initPWA() {
117
 
118
  css_style = """
119
  body { background-color: #0d1117 !important; color: white !important; }
120
- .api-box { background: #161b22; border-radius: 8px; padding: 10px; border: 1px solid #30363d; margin-top: -10px; margin-bottom: 10px; }
121
  .center-btn { justify-content: center !important; gap: 8px !important; }
122
  .small-btn { max-width: 85px !important; min-width: 70px !important; font-size: 13px !important; padding: 4px !important; }
123
  .translate-btn { min-width: 150px !important; font-weight: bold !important; background: linear-gradient(45deg, #1e40af, #3b82f6) !important; color: white !important; }
@@ -130,12 +138,9 @@ with gr.Blocks(css=css_style, head=f"<script>{pwa_js}</script>") as demo:
130
  with gr.Column():
131
  with gr.Row():
132
  engine_opt = gr.Radio(choices=["πŸ’Ž Gemini", "πŸ¦™ Llama", "🦁 SEA-LION"], value="πŸ’Ž Gemini", label="αž‡αŸ’αžšαžΎαžŸαžšαžΎαžŸαž˜αŸ‰αžΌαžŠαŸ‚αž›")
133
- lang_opt = gr.Dropdown(
134
- choices=["πŸ‡°πŸ‡­ Khmer", "πŸ‡ΊπŸ‡Έ English", "πŸ‡ΉπŸ‡­ Thai", "πŸ‡¨πŸ‡³ Chinese", "πŸ‡―πŸ‡΅ Japanese", "πŸ‡°πŸ‡· Korean", "πŸ‡»πŸ‡³ Vietnamese"],
135
- value="πŸ‡°πŸ‡­ Khmer", label="αž”αž€αž”αŸ’αžšαŸ‚αž‡αžΆαž—αžΆαžŸαžΆ"
136
- )
137
 
138
- # αž”αŸ’αžšαž’αž”αŸ‹αž”αž„αŸ’αž αžΆαž‰ Status API Keys
139
  api_status_ui = gr.HTML(AIService.get_status_html("πŸ’Ž Gemini"), elem_classes="api-box")
140
 
141
  input_text = gr.Textbox(label="αž’αžαŸ’αžαž”αž‘αžŠαžΎαž˜", lines=5, placeholder="αž”αž·αž‘αž—αŸ’αž‡αžΆαž”αŸ‹ SRT αž¬αž’αžαŸ’αžαž”αž‘...")
@@ -147,9 +152,7 @@ with gr.Blocks(css=css_style, head=f"<script>{pwa_js}</script>") as demo:
147
 
148
  output_text = gr.Textbox(label="αž›αž‘αŸ’αž’αž•αž›", lines=8, interactive=False)
149
 
150
- # αž”αŸ’αžαžΌαžš Status αž—αŸ’αž›αžΎαž„αž–αŸαž›αžŠαžΌαžšαž˜αŸ‰αžΌαžŠαŸ‚αž›
151
  engine_opt.change(fn=AIService.get_status_html, inputs=engine_opt, outputs=api_status_ui)
152
-
153
  submit_btn.click(fn=translator_hub, inputs=[input_text, lang_opt, engine_opt], outputs=output_text)
154
  clear_btn.click(lambda: ["", ""], None, [input_text, output_text])
155
  copy_btn.click(None, inputs=output_text, js="(v) => { if(v){ navigator.clipboard.writeText(v); alert('αž”αžΆαž“αž…αž˜αŸ’αž›αž„! πŸŽ†'); } }")
 
1
  import os
2
  import requests
3
  import gradio as gr
 
4
 
5
  # ==========================================
6
+ # ៑. αž•αŸ’αž“αŸ‚αž€αž”αž…αŸ’αž…αŸαž€αž‘αŸαžŸ API (Auto-Detect Environment Keys)
7
  # ==========================================
8
  class AIService:
9
+ @staticmethod
10
+ def get_all_keys(prefix):
11
+ """αžŸαŸ’αžœαŸ‚αž„αžšαž€ Key αž‘αžΆαŸ†αž„αž’αžŸαŸ‹αžŠαŸ‚αž›αž˜αžΆαž“αž•αŸ’αžŠαžΎαž˜αžŠαŸ„αž™ prefix (αž§αž‘αžΆαž αžšαžŽαŸ GEMINI_API_KEY_)"""
12
+ keys = []
13
+ i = 1
14
+ while True:
15
+ key = os.environ.get(f"{prefix}{i}")
16
+ if key:
17
+ keys.append(key)
18
+ i += 1
19
+ else:
20
+ break
21
+ # αž€αžšαžŽαžΈαž–αž·αžŸαŸαžŸαžŸαž˜αŸ’αžšαžΆαž”αŸ‹ SEA-LION αžŠαŸ‚αž›αž˜αžΆαž“αžαŸ‚αž˜αž½αž™
22
+ if not keys and os.environ.get(prefix.replace("_KEY_", "_KEY")):
23
+ keys.append(os.environ.get(prefix.replace("_KEY_", "_KEY")))
24
+ return keys
25
+
26
  @staticmethod
27
  def get_status_html(engine):
28
+ """αž”αž„αŸ’αž€αžΎαž UI αž—αŸ’αž›αžΎαž„αžŸαž‰αŸ’αž‰αžΆαžŠαŸ„αž™αžŸαŸ’αžœαŸαž™αž”αŸ’αžšαžœαžαŸ’αžαž·αžαžΆαž˜αž…αŸ†αž“αž½αž“ Key αžŠαŸ‚αž›αžšαž€αžƒαžΎαž‰"""
29
  if "Gemini" in engine:
30
+ keys = AIService.get_all_keys("GEMINI_API_KEY_")
31
+ label = "Gemini Active Keys"
32
  elif "Llama" in engine:
33
+ keys = AIService.get_all_keys("GROQ_API_KEY_")
34
+ label = "Llama Active Keys"
35
  else:
36
+ keys = [os.environ.get("SEA_LION_API_KEY")] if os.environ.get("SEA_LION_API_KEY") else []
37
+ label = "SEA-LION Active Key"
 
38
 
39
+ count = len(keys)
40
  html = f"<div style='display: flex; gap: 10px; align-items: center; margin-bottom: 10px;'>"
41
+ html += f"<b style='color: #94a3b8; font-size: 12px;'>{label} ({count}):</b>"
42
+
43
+ if count == 0:
44
+ html += f"<div title='No Key Found' style='width: 12px; height: 12px; background: #ef4444; border-radius: 50%; box-shadow: 0 0 5px #ef4444;'></div>"
45
+ else:
46
+ for i in range(count):
47
+ html += f"<div title='Key {i+1} Active' style='width: 12px; height: 12px; background: #22c55e; border-radius: 50%; box-shadow: 0 0 8px #22c55e; animation: pulse 1.5s infinite;'></div>"
 
48
  html += "</div>"
49
  return html
50
 
51
  @staticmethod
52
  def translate_gemini(prompt):
53
+ keys = AIService.get_all_keys("GEMINI_API_KEY_")
54
  for key in keys:
55
  try:
56
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent?key={key}"
57
+ instruction = "Return ONLY direct translation. Be natural. Provide alternatives on repeat. Preserve format."
58
  res = requests.post(url, json={"contents": [{"parts": [{"text": f"{instruction}\n\n{prompt}"}]}]}, timeout=15)
59
  if res.status_code == 200:
60
  return res.json()['candidates'][0]['content']['parts'][0]['text'].strip()
61
  except: continue
62
+ return "❌ Gemini API Error: No working keys found."
63
 
64
  @staticmethod
65
  def translate_llama(prompt):
66
+ keys = AIService.get_all_keys("GROQ_API_KEY_")
67
  for key in keys:
68
  try:
69
  headers = {"Authorization": f"Bearer {key}", "Content-Type": "application/json"}
70
  payload = {
71
  "model": "llama-3.3-70b-versatile",
72
+ "messages": [{"role": "system", "content": "DIRECT TRANSLATION ONLY. Be natural."}, {"role": "user", "content": prompt}],
 
 
 
73
  "temperature": 0.7
74
  }
75
  res = requests.post("https://api.groq.com/openai/v1/chat/completions", headers=headers, json=payload, timeout=15)
 
87
  headers = {"Authorization": f"Bearer {key}", "Content-Type": "application/json"}
88
  payload = {
89
  "model": "aisingapore/Gemma-SEA-LION-v4-27B-IT",
90
+ "messages": [{"role": "user", "content": f"Translate (Khmer ONLY, natural): {prompt}"}],
91
  "temperature": 0.7
92
  }
93
  res = requests.post(url, headers=headers, json=payload, timeout=20)
 
99
  def translator_hub(text, target_lang, engine):
100
  if not text.strip(): return ""
101
  lang_name = target_lang.split(" ")[-1]
102
+ prompt = f"Target: {lang_name}\nText: {text}"
 
103
  if "Gemini" in engine: return AIService.translate_gemini(prompt)
104
  elif "Llama" in engine: return AIService.translate_llama(prompt)
105
  else: return AIService.translate_sealion(prompt)
106
 
107
  # ==========================================
108
+ # ្. αžŸαŸ’αž‚αŸ’αžšαžΈαž” UI & CSS (αžšαž€αŸ’αžŸαžΆαž€αžΌαžŠαžŠαžΎαž˜αž˜αŸ)
109
  # ==========================================
110
  pwa_js = """
111
  function initPWA() {
112
  const s = document.createElement('script');
113
  s.src = 'https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js';
114
  document.head.appendChild(s);
 
 
115
  const style = document.createElement('style');
116
  style.innerHTML = `@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }`;
117
  document.head.appendChild(style);
 
118
  window.addEventListener('click', (e) => {
119
  if (typeof confetti === 'function') {
120
  confetti({ particleCount: 50, spread: 60, origin: { x: e.clientX / window.innerWidth, y: e.clientY / window.innerHeight } });
 
125
 
126
  css_style = """
127
  body { background-color: #0d1117 !important; color: white !important; }
128
+ .api-box { background: #161b22; border-radius: 8px; padding: 10px; border: 1px solid #30363d; margin-bottom: 15px; }
129
  .center-btn { justify-content: center !important; gap: 8px !important; }
130
  .small-btn { max-width: 85px !important; min-width: 70px !important; font-size: 13px !important; padding: 4px !important; }
131
  .translate-btn { min-width: 150px !important; font-weight: bold !important; background: linear-gradient(45deg, #1e40af, #3b82f6) !important; color: white !important; }
 
138
  with gr.Column():
139
  with gr.Row():
140
  engine_opt = gr.Radio(choices=["πŸ’Ž Gemini", "πŸ¦™ Llama", "🦁 SEA-LION"], value="πŸ’Ž Gemini", label="αž‡αŸ’αžšαžΎαžŸαžšαžΎαžŸαž˜αŸ‰αžΌαžŠαŸ‚αž›")
141
+ lang_opt = gr.Dropdown(choices=["πŸ‡°πŸ‡­ Khmer", "πŸ‡ΊπŸ‡Έ English", "πŸ‡ΉπŸ‡­ Thai", "πŸ‡¨πŸ‡³ Chinese", "πŸ‡―πŸ‡΅ Japanese", "πŸ‡°πŸ‡· Korean", "πŸ‡»πŸ‡³ Vietnamese"], value="πŸ‡°πŸ‡­ Khmer", label="αž”αž€αž”αŸ’αžšαŸ‚αž‡αžΆαž—αžΆαžŸαžΆ")
 
 
 
142
 
143
+ # UI Status Display
144
  api_status_ui = gr.HTML(AIService.get_status_html("πŸ’Ž Gemini"), elem_classes="api-box")
145
 
146
  input_text = gr.Textbox(label="αž’αžαŸ’αžαž”αž‘αžŠαžΎαž˜", lines=5, placeholder="αž”αž·αž‘αž—αŸ’αž‡αžΆαž”αŸ‹ SRT αž¬αž’αžαŸ’αžαž”αž‘...")
 
152
 
153
  output_text = gr.Textbox(label="αž›αž‘αŸ’αž’αž•αž›", lines=8, interactive=False)
154
 
 
155
  engine_opt.change(fn=AIService.get_status_html, inputs=engine_opt, outputs=api_status_ui)
 
156
  submit_btn.click(fn=translator_hub, inputs=[input_text, lang_opt, engine_opt], outputs=output_text)
157
  clear_btn.click(lambda: ["", ""], None, [input_text, output_text])
158
  copy_btn.click(None, inputs=output_text, js="(v) => { if(v){ navigator.clipboard.writeText(v); alert('αž”αžΆαž“αž…αž˜αŸ’αž›αž„! πŸŽ†'); } }")