deankk commited on
Commit
8df6ea0
·
verified ·
1 Parent(s): 20d8337

Update app.py

Browse files

Tiga perubahan saja:

1. theme dan css dipindah dari gr.Blocks() ke demo.launch()
2. show_copy_button=True dihapus dari Textbox
3. gr.CopyButton(value=box) ditambahkan di samping setiap Textbox

Files changed (1) hide show
  1. app.py +37 -59
app.py CHANGED
@@ -24,18 +24,17 @@ import os
24
  # KONFIGURASI MODEL
25
  # ══════════════════════════════════════════════════════
26
 
27
- # Pilih salah satu (hapus tanda # di depan yang mau dipakai):
28
- MODEL_NAME = "Qwen/Qwen2.5-3B-Instruct" # ← RECOMMENDED, bagus buat Indo
29
- # MODEL_NAME = "Qwen/Qwen2.5-1.5B-Instruct" # ← Lebih ringan, cukup bagus
30
- # MODEL_NAME = "meta-llama/Llama-3.2-1B-Instruct" # ← Paling ringan, kualitas lower
31
- # MODEL_NAME = "google/gemma-2-2b-it" # ← Alternatif bagus
32
 
33
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
34
  CHAT_API = f"https://api-inference.huggingface.co/models/{MODEL_NAME}/v1/chat/completions"
35
  TEXT_API = f"https://api-inference.huggingface.co/models/{MODEL_NAME}"
36
 
37
  # ══════════════════════════════════════════════════════
38
- # DATA PLATFORM, GAYA, BAHASA
39
  # ══════════════════════════════════════════════════════
40
 
41
  PLATFORMS = {
@@ -90,18 +89,16 @@ ATURAN KETAT (WAJIB DIIKUTI):
90
 
91
 
92
  # ══════════════════════════════════════════════════════
93
- # LOGIC — API CALL & GENERATE
94
  # ══════════════════════════════════════════════════════
95
 
96
  def call_api(messages):
97
- """Panggil HF Inference API (chat dulu, fallback ke text)"""
98
  headers = {
99
  "Authorization": f"Bearer {HF_TOKEN}",
100
  "Content-Type": "application/json"
101
  }
102
  params = {"max_tokens": 1000, "temperature": 0.85, "top_p": 0.9}
103
 
104
- # ── Attempt 1: Chat Completions API ──
105
  try:
106
  resp = requests.post(
107
  CHAT_API, headers=headers,
@@ -119,7 +116,6 @@ def call_api(messages):
119
  except Exception:
120
  pass
121
 
122
- # ── Attempt 2: Text Generation API (fallback) ──
123
  try:
124
  prompt = f"{messages[0]['content']}\n\n{messages[1]['content']}"
125
  resp = requests.post(
@@ -139,7 +135,6 @@ def call_api(messages):
139
  data = resp.json()
140
  if isinstance(data, list) and "generated_text" in data[0]:
141
  text = data[0]["generated_text"]
142
- # Strip prompt kalau ikut ke-return
143
  if prompt in text:
144
  text = text.replace(prompt, "").strip()
145
  return text
@@ -156,7 +151,6 @@ def call_api(messages):
156
 
157
 
158
  def clean_text(text):
159
- """Hapus preamble yang sering muncul dari model"""
160
  if not text:
161
  return text
162
  unwanted = [
@@ -178,14 +172,12 @@ def clean_text(text):
178
 
179
 
180
  def parse_captions(raw_text, jumlah):
181
- """Parse response jadi list caption individual"""
182
  text = clean_text(raw_text)
183
  parts = [p.strip() for p in text.split("---") if p.strip()]
184
 
185
  captions = []
186
  for p in parts:
187
  p = p.strip()
188
- # Hapus nomor di awal kalau ada
189
  if len(p) > 2 and p[0].isdigit() and p[1] in ".)":
190
  p = p[2:].strip()
191
  if len(p) > 15:
@@ -200,17 +192,10 @@ def parse_captions(raw_text, jumlah):
200
 
201
 
202
  def generate_caption(konteks, platform, gaya, bahasa, jumlah):
203
- """Fungsi utama generate — return values + visibility"""
204
- error_all_visible = tuple(
205
- [gr.update(visible=True)] * 5
206
- )
207
-
208
- # Validasi kosong
209
  if not konteks or not konteks.strip():
210
  vals = ["⚠️ Tulis dulu deskripsi foto/konten kamu di kolom atas!"] + [""] * 4
211
  return tuple(gr.update(value=v, visible=True) for v in vals)
212
 
213
- # Validasi token
214
  if not HF_TOKEN:
215
  msg = (
216
  "⚠️ TOKEN BELUM DISET!\n\n"
@@ -223,7 +208,6 @@ def generate_caption(konteks, platform, gaya, bahasa, jumlah):
223
  vals = [msg] + [""] * 4
224
  return tuple(gr.update(value=v, visible=True) for v in vals)
225
 
226
- # Bangun user prompt
227
  user_msg = f"""PLATFORM: {platform}
228
  {PLATFORMS[platform]}
229
 
@@ -243,7 +227,6 @@ BUAT TEPAT {jumlah} CAPTION YANG BERBEDA."""
243
  {"role": "user", "content": user_msg}
244
  ]
245
 
246
- # Panggil API
247
  result = call_api(messages)
248
 
249
  error_map = {
@@ -257,10 +240,8 @@ BUAT TEPAT {jumlah} CAPTION YANG BERBEDA."""
257
  vals = [error_map[result]] + [""] * 4
258
  return tuple(gr.update(value=v, visible=True) for v in vals)
259
 
260
- # Parse caption
261
  captions = parse_captions(result, jumlah)
262
 
263
- # Bangun output dengan value + visibility
264
  output = []
265
  for i in range(5):
266
  if i < jumlah:
@@ -272,17 +253,15 @@ BUAT TEPAT {jumlah} CAPTION YANG BERBEDA."""
272
 
273
 
274
  def update_visibility(jumlah):
275
- """Update visibility saat dropdown jumlah berubah"""
276
  return tuple(gr.update(visible=(i < jumlah)) for i in range(5))
277
 
278
 
279
  def fill_preset(name):
280
- """Isi konteks dari tombol preset"""
281
  return PRESETS.get(name, "")
282
 
283
 
284
  # ══════════════════════════════════════════════════════
285
- # UI — GRADIO INTERFACE
286
  # ══════════════════════════════════════════════════════
287
 
288
  CUSTOM_CSS = """
@@ -309,18 +288,8 @@ CUSTOM_CSS = """
309
  }
310
  """
311
 
312
- with gr.Blocks(
313
- title="CapGen AI — Caption Generator Indonesia",
314
- theme=gr.themes.Soft(
315
- primary_hue="orange",
316
- secondary_hue="amber",
317
- neutral_hue="stone",
318
- font=gr.themes.GoogleFont("Inter"),
319
- ),
320
- css=CUSTOM_CSS,
321
- ) as demo:
322
 
323
- # ── HEADER ──
324
  gr.Markdown("""
325
  <div style="text-align:center; padding:8px 0 4px 0;">
326
  <h1 style="margin:0;">✍️ CapGen AI</h1>
@@ -333,10 +302,8 @@ with gr.Blocks(
333
  </div>
334
  """)
335
 
336
- # ── MAIN: 2 KOLOM ──
337
  with gr.Row(equal_height=False):
338
 
339
- # ── KOLOM KIRI: INPUT ──
340
  with gr.Column(scale=1):
341
 
342
  gr.Markdown("### 📝 Ceritakan Foto/Konten Kamu")
@@ -392,42 +359,47 @@ with gr.Blocks(
392
  elem_id="btn-generate",
393
  )
394
 
395
- # ── KOLOM KANAN: OUTPUT ──
396
  with gr.Column(scale=1):
397
  gr.Markdown("### 📋 Hasil Caption")
398
  gr.Markdown(
399
  '<span style="font-size:12px;color:#999;">'
400
- 'Klik ikon 📋 di pojok kanan untuk copy</span>'
401
  )
402
 
403
  caption_boxes = []
 
404
  for i in range(5):
405
- box = gr.Textbox(
406
- label=f"Caption {i + 1}",
407
- lines=4,
408
- max_lines=8,
409
- show_copy_button=True,
410
- interactive=False,
411
- visible=(i < 3),
412
- elem_classes=["caption-output"],
413
- )
414
- caption_boxes.append(box)
 
 
 
415
 
416
- # Visibility update saat jumlah berubah
417
  jumlah.change(
418
  fn=update_visibility,
419
  inputs=[jumlah],
420
- outputs=caption_boxes,
421
  )
422
 
423
- # Generate caption
424
  btn_generate.click(
425
  fn=generate_caption,
426
  inputs=[konteks, platform, gaya, bahasa, jumlah],
427
  outputs=caption_boxes,
428
  )
 
 
 
 
 
429
 
430
- # ── FOOTER ──
431
  gr.Markdown(
432
  '<div class="footer-text">'
433
  "Dibuat dengan ❤️ untuk komunitas Indonesia · "
@@ -435,5 +407,11 @@ with gr.Blocks(
435
  "</div>"
436
  )
437
 
438
- # ── JALANKAN ──
439
- demo.launch()
 
 
 
 
 
 
 
24
  # KONFIGURASI MODEL
25
  # ══════════════════════════════════════════════════════
26
 
27
+ MODEL_NAME = "Qwen/Qwen2.5-3B-Instruct"
28
+ # MODEL_NAME = "Qwen/Qwen2.5-1.5B-Instruct"
29
+ # MODEL_NAME = "meta-llama/Llama-3.2-1B-Instruct"
30
+ # MODEL_NAME = "google/gemma-2-2b-it"
 
31
 
32
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
33
  CHAT_API = f"https://api-inference.huggingface.co/models/{MODEL_NAME}/v1/chat/completions"
34
  TEXT_API = f"https://api-inference.huggingface.co/models/{MODEL_NAME}"
35
 
36
  # ══════════════════════════════════════════════════════
37
+ # DATA
38
  # ══════════════════════════════════════════════════════
39
 
40
  PLATFORMS = {
 
89
 
90
 
91
  # ══════════════════════════════════════════════════════
92
+ # LOGIC
93
  # ══════════════════════════════════════════════════════
94
 
95
  def call_api(messages):
 
96
  headers = {
97
  "Authorization": f"Bearer {HF_TOKEN}",
98
  "Content-Type": "application/json"
99
  }
100
  params = {"max_tokens": 1000, "temperature": 0.85, "top_p": 0.9}
101
 
 
102
  try:
103
  resp = requests.post(
104
  CHAT_API, headers=headers,
 
116
  except Exception:
117
  pass
118
 
 
119
  try:
120
  prompt = f"{messages[0]['content']}\n\n{messages[1]['content']}"
121
  resp = requests.post(
 
135
  data = resp.json()
136
  if isinstance(data, list) and "generated_text" in data[0]:
137
  text = data[0]["generated_text"]
 
138
  if prompt in text:
139
  text = text.replace(prompt, "").strip()
140
  return text
 
151
 
152
 
153
  def clean_text(text):
 
154
  if not text:
155
  return text
156
  unwanted = [
 
172
 
173
 
174
  def parse_captions(raw_text, jumlah):
 
175
  text = clean_text(raw_text)
176
  parts = [p.strip() for p in text.split("---") if p.strip()]
177
 
178
  captions = []
179
  for p in parts:
180
  p = p.strip()
 
181
  if len(p) > 2 and p[0].isdigit() and p[1] in ".)":
182
  p = p[2:].strip()
183
  if len(p) > 15:
 
192
 
193
 
194
  def generate_caption(konteks, platform, gaya, bahasa, jumlah):
 
 
 
 
 
 
195
  if not konteks or not konteks.strip():
196
  vals = ["⚠️ Tulis dulu deskripsi foto/konten kamu di kolom atas!"] + [""] * 4
197
  return tuple(gr.update(value=v, visible=True) for v in vals)
198
 
 
199
  if not HF_TOKEN:
200
  msg = (
201
  "⚠️ TOKEN BELUM DISET!\n\n"
 
208
  vals = [msg] + [""] * 4
209
  return tuple(gr.update(value=v, visible=True) for v in vals)
210
 
 
211
  user_msg = f"""PLATFORM: {platform}
212
  {PLATFORMS[platform]}
213
 
 
227
  {"role": "user", "content": user_msg}
228
  ]
229
 
 
230
  result = call_api(messages)
231
 
232
  error_map = {
 
240
  vals = [error_map[result]] + [""] * 4
241
  return tuple(gr.update(value=v, visible=True) for v in vals)
242
 
 
243
  captions = parse_captions(result, jumlah)
244
 
 
245
  output = []
246
  for i in range(5):
247
  if i < jumlah:
 
253
 
254
 
255
  def update_visibility(jumlah):
 
256
  return tuple(gr.update(visible=(i < jumlah)) for i in range(5))
257
 
258
 
259
  def fill_preset(name):
 
260
  return PRESETS.get(name, "")
261
 
262
 
263
  # ══════════════════════════════════════════════════════
264
+ # UI
265
  # ══════════════════════════════════════════════════════
266
 
267
  CUSTOM_CSS = """
 
288
  }
289
  """
290
 
291
+ with gr.Blocks() as demo:
 
 
 
 
 
 
 
 
 
292
 
 
293
  gr.Markdown("""
294
  <div style="text-align:center; padding:8px 0 4px 0;">
295
  <h1 style="margin:0;">✍️ CapGen AI</h1>
 
302
  </div>
303
  """)
304
 
 
305
  with gr.Row(equal_height=False):
306
 
 
307
  with gr.Column(scale=1):
308
 
309
  gr.Markdown("### 📝 Ceritakan Foto/Konten Kamu")
 
359
  elem_id="btn-generate",
360
  )
361
 
 
362
  with gr.Column(scale=1):
363
  gr.Markdown("### 📋 Hasil Caption")
364
  gr.Markdown(
365
  '<span style="font-size:12px;color:#999;">'
366
+ 'Klik tombol 📋 untuk copy tiap caption</span>'
367
  )
368
 
369
  caption_boxes = []
370
+ caption_groups = []
371
  for i in range(5):
372
+ with gr.Group(visible=(i < 3)) as group:
373
+ with gr.Row():
374
+ box = gr.Textbox(
375
+ label=f"Caption {i + 1}",
376
+ lines=4,
377
+ max_lines=8,
378
+ interactive=False,
379
+ elem_classes=["caption-output"],
380
+ scale=5,
381
+ )
382
+ copy_btn = gr.CopyButton(value=box, scale=1)
383
+ caption_boxes.append(box)
384
+ caption_groups.append(group)
385
 
 
386
  jumlah.change(
387
  fn=update_visibility,
388
  inputs=[jumlah],
389
+ outputs=caption_groups,
390
  )
391
 
 
392
  btn_generate.click(
393
  fn=generate_caption,
394
  inputs=[konteks, platform, gaya, bahasa, jumlah],
395
  outputs=caption_boxes,
396
  )
397
+ btn_generate.click(
398
+ fn=update_visibility,
399
+ inputs=[jumlah],
400
+ outputs=caption_groups,
401
+ )
402
 
 
403
  gr.Markdown(
404
  '<div class="footer-text">'
405
  "Dibuat dengan ❤️ untuk komunitas Indonesia · "
 
407
  "</div>"
408
  )
409
 
410
+ demo.launch(
411
+ theme=gr.themes.Soft(
412
+ primary_hue="orange",
413
+ secondary_hue="amber",
414
+ neutral_hue="stone",
415
+ ),
416
+ css=CUSTOM_CSS,
417
+ )