Akhmad123 commited on
Commit
42ae0f7
·
verified ·
1 Parent(s): eca2c8f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +265 -56
app.py CHANGED
@@ -1,12 +1,16 @@
1
- import torch
2
- import gradio as gr
3
  import gc
4
- from diffusers import StableDiffusionPipeline, StableDiffusionImg2ImgPipeline
5
- from PIL import Image
6
  import uuid
7
  import time
8
  import random
9
- import os
 
 
 
 
 
 
 
10
 
11
  # =========================
12
  # DEVICE
@@ -14,10 +18,17 @@ import os
14
  device = "cpu"
15
 
16
  # =========================
17
- # MODEL CONFIG
18
  # =========================
19
  MODEL_ID = "Lykon/dreamshaper-8"
20
 
 
 
 
 
 
 
 
21
  # =========================
22
  # GLOBAL STATE
23
  # =========================
@@ -25,26 +36,34 @@ current_pipe = None
25
  current_mode = None
26
  history = []
27
 
 
28
  # =========================
29
  # STYLE PRESETS
30
  # =========================
31
  STYLE_PRESETS = {
32
  "None": "",
33
- "Realistic": "photorealistic, natural lighting, depth of field",
34
- "Anime": "anime style, vibrant colors, clean lines",
35
- "Cinematic": "cinematic lighting, dramatic shadows, movie composition",
 
 
36
  }
37
 
 
38
  # =========================
39
  # NEGATIVE PROMPT
40
  # =========================
41
  NEGATIVE_PROMPT = (
42
  "worst quality, low quality, blurry, bad anatomy, bad proportions, "
43
- "extra limbs, distorted, deformed, ugly, watermark"
 
 
44
  )
45
 
 
46
  # =========================
47
- # TRANSLATE ID TO EN SIMPLE
 
48
  # =========================
49
  def translate_prompt(prompt):
50
  if not prompt:
@@ -52,17 +71,25 @@ def translate_prompt(prompt):
52
 
53
  mapping = {
54
  "pria": "man",
 
55
  "wanita": "woman",
 
56
  "anak": "child",
 
 
57
  "gunung": "mountain",
58
  "pantai": "beach",
59
  "kota": "city",
60
  "malam": "night",
61
  "siang": "day",
 
 
 
62
  "laut": "sea",
63
  "langit": "sky",
64
  "hutan": "forest",
65
  "mobil": "car",
 
66
  "rumah": "house",
67
  "jalan": "street",
68
  "sungai": "river",
@@ -72,7 +99,29 @@ def translate_prompt(prompt):
72
  "burung": "bird",
73
  "bunga": "flower",
74
  "pohon": "tree",
75
- "robot": "robot"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
77
 
78
  prompt = prompt.lower()
@@ -91,12 +140,14 @@ def detect_type(prompt):
91
 
92
  portrait_keywords = [
93
  "person", "man", "woman", "girl", "boy",
94
- "face", "portrait", "child"
95
  ]
96
 
97
  scene_keywords = [
98
  "mountain", "city", "forest", "beach", "sky",
99
- "sea", "street", "river", "lake", "tree"
 
 
100
  ]
101
 
102
  if any(k in p for k in portrait_keywords):
@@ -109,31 +160,35 @@ def detect_type(prompt):
109
 
110
 
111
  # =========================
112
- # AI PROMPT ENGINE
113
  # =========================
114
- def build_prompt(prompt, style):
 
 
 
115
  prompt = translate_prompt(prompt)
116
  style_text = STYLE_PRESETS.get(style, "")
117
 
118
- base = "masterpiece, best quality, ultra detailed, sharp focus, 4k"
119
- detail = "intricate details, HDR, high detail"
120
 
121
  ptype = detect_type(prompt)
122
 
123
  if ptype == "portrait":
124
  extra = (
125
- "detailed face, skin texture, soft lighting, "
126
- "portrait composition, depth of field"
 
127
  )
128
  elif ptype == "scene":
129
  extra = (
130
  "wide shot, cinematic composition, environmental detail, "
131
- "atmospheric lighting"
132
  )
133
  else:
134
  extra = (
135
- "product photography, centered composition, "
136
- "studio lighting"
137
  )
138
 
139
  parts = [
@@ -147,6 +202,122 @@ def build_prompt(prompt, style):
147
  return ", ".join([p for p in parts if p])
148
 
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  # =========================
151
  # LOAD PIPELINE DREAMSHAPER ONLY
152
  # =========================
@@ -280,6 +451,7 @@ def prepare_img2img_input(input_image):
280
  def generate_text_to_image(
281
  prompt,
282
  style,
 
283
  steps,
284
  width,
285
  height,
@@ -288,7 +460,9 @@ def generate_text_to_image(
288
  ):
289
  pipe = load_pipe("txt2img")
290
 
291
- final_prompt = build_prompt(prompt, style)
 
 
292
 
293
  width = normalize_size(width)
294
  height = normalize_size(height)
@@ -313,7 +487,7 @@ def generate_text_to_image(
313
 
314
  file_path = save_image(image)
315
 
316
- return image, file_path, seed, history
317
 
318
 
319
  # =========================
@@ -322,6 +496,7 @@ def generate_text_to_image(
322
  def generate_image_to_image(
323
  prompt,
324
  style,
 
325
  steps,
326
  seed,
327
  input_image,
@@ -329,11 +504,13 @@ def generate_image_to_image(
329
  up
330
  ):
331
  if input_image is None:
332
- return None, None, None, history
333
 
334
  pipe = load_pipe("img2img")
335
 
336
- final_prompt = build_prompt(prompt, style)
 
 
337
 
338
  seed = normalize_seed(seed)
339
  input_image = prepare_img2img_input(input_image)
@@ -357,7 +534,7 @@ def generate_image_to_image(
357
 
358
  file_path = save_image(image)
359
 
360
- return image, file_path, seed, history
361
 
362
 
363
  # =========================
@@ -366,6 +543,7 @@ def generate_image_to_image(
366
  def run_txt2img(
367
  prompt,
368
  style,
 
369
  steps,
370
  width,
371
  height,
@@ -377,6 +555,7 @@ def run_txt2img(
377
  return generate_text_to_image(
378
  prompt,
379
  style,
 
380
  steps,
381
  width,
382
  height,
@@ -388,12 +567,13 @@ def run_txt2img(
388
  gc.collect()
389
  time.sleep(2)
390
 
391
- return None, None, None, history
392
 
393
 
394
  def run_img2img(
395
  prompt,
396
  style,
 
397
  steps,
398
  seed,
399
  input_image,
@@ -405,6 +585,7 @@ def run_img2img(
405
  return generate_image_to_image(
406
  prompt,
407
  style,
 
408
  steps,
409
  seed,
410
  input_image,
@@ -416,7 +597,7 @@ def run_img2img(
416
  gc.collect()
417
  time.sleep(2)
418
 
419
- return None, None, None, history
420
 
421
 
422
  # =========================
@@ -454,23 +635,31 @@ def clear_history():
454
  return [], None, None
455
 
456
 
 
 
 
 
 
 
 
 
 
 
457
  # =========================
458
  # UI
459
  # =========================
460
  with gr.Blocks() as demo:
461
- gr.Markdown("# 🚀 AI Image Studio - DreamShaper Edition")
 
462
  gr.Markdown(
463
  """
464
- Semua fitur menggunakan **DreamShaper**.
465
-
466
- Fitur:
467
- - Text to Image
468
- - Image to Image
469
- - History otomatis
470
- - Re-use gambar dari History ke Image to Image
471
  """
472
  )
473
 
 
 
474
  selected_history_index = gr.State(value=None)
475
 
476
  with gr.Tabs(selected="txt2img") as tabs:
@@ -481,8 +670,8 @@ with gr.Blocks() as demo:
481
  with gr.Tab("Text to Image", id="txt2img"):
482
  txt_prompt = gr.Textbox(
483
  lines=3,
484
- label="Prompt",
485
- placeholder="Contoh: pria berdiri di kota futuristik malam hari"
486
  )
487
 
488
  txt_style = gr.Dropdown(
@@ -491,6 +680,11 @@ with gr.Blocks() as demo:
491
  label="Style"
492
  )
493
 
 
 
 
 
 
494
  txt_steps = gr.Slider(
495
  minimum=10,
496
  maximum=30,
@@ -535,6 +729,12 @@ with gr.Blocks() as demo:
535
  label="Seed Used"
536
  )
537
 
 
 
 
 
 
 
538
  txt_file_output = gr.File(
539
  label="Download Image"
540
  )
@@ -545,8 +745,8 @@ with gr.Blocks() as demo:
545
  with gr.Tab("Image to Image", id="img2img"):
546
  img_prompt = gr.Textbox(
547
  lines=3,
548
- label="Prompt",
549
- placeholder="Contoh: ubah menjadi cinematic style, lighting dramatis"
550
  )
551
 
552
  img_input = gr.Image(
@@ -554,6 +754,17 @@ with gr.Blocks() as demo:
554
  label="Input Image"
555
  )
556
 
 
 
 
 
 
 
 
 
 
 
 
557
  img_strength = gr.Slider(
558
  minimum=0.1,
559
  maximum=1.0,
@@ -562,12 +773,6 @@ with gr.Blocks() as demo:
562
  label="Strength"
563
  )
564
 
565
- img_style = gr.Dropdown(
566
- choices=list(STYLE_PRESETS.keys()),
567
- value="Realistic",
568
- label="Style"
569
- )
570
-
571
  img_steps = gr.Slider(
572
  minimum=10,
573
  maximum=30,
@@ -596,6 +801,12 @@ with gr.Blocks() as demo:
596
  label="Seed Used"
597
  )
598
 
 
 
 
 
 
 
599
  img_file_output = gr.File(
600
  label="Download Image"
601
  )
@@ -606,8 +817,8 @@ with gr.Blocks() as demo:
606
  with gr.Tab("History", id="history"):
607
  gr.Markdown(
608
  """
609
- Gambar yang sudah dibuat dari **Text to Image** atau **Image to Image**
610
- akan otomatis muncul di sini.
611
 
612
  Cara memakai ulang:
613
  1. Klik salah satu gambar di History
@@ -638,12 +849,12 @@ with gr.Blocks() as demo:
638
  # EVENTS
639
  # =========================
640
 
641
- # Text to Image event
642
  txt_button.click(
643
  fn=run_txt2img,
644
  inputs=[
645
  txt_prompt,
646
  txt_style,
 
647
  txt_steps,
648
  txt_width,
649
  txt_height,
@@ -654,16 +865,17 @@ with gr.Blocks() as demo:
654
  txt_result,
655
  txt_file_output,
656
  txt_seed_output,
 
657
  history_gallery
658
  ]
659
  )
660
 
661
- # Image to Image event
662
  img_button.click(
663
  fn=run_img2img,
664
  inputs=[
665
  img_prompt,
666
  img_style,
 
667
  img_steps,
668
  img_seed,
669
  img_input,
@@ -674,11 +886,11 @@ with gr.Blocks() as demo:
674
  img_result,
675
  img_file_output,
676
  img_seed_output,
 
677
  history_gallery
678
  ]
679
  )
680
 
681
- # Select history image
682
  history_gallery.select(
683
  fn=select_history,
684
  outputs=[
@@ -687,7 +899,6 @@ with gr.Blocks() as demo:
687
  ]
688
  )
689
 
690
- # Reuse selected history image to Image to Image tab
691
  reuse_button.click(
692
  fn=reuse_history,
693
  inputs=[
@@ -699,7 +910,6 @@ with gr.Blocks() as demo:
699
  ]
700
  )
701
 
702
- # Clear history
703
  clear_history_button.click(
704
  fn=clear_history,
705
  inputs=[],
@@ -710,7 +920,6 @@ with gr.Blocks() as demo:
710
  ]
711
  )
712
 
713
- # Load initial history
714
  demo.load(
715
  fn=lambda: history,
716
  inputs=[],
 
1
+ import os
 
2
  import gc
 
 
3
  import uuid
4
  import time
5
  import random
6
+ import requests
7
+
8
+ import torch
9
+ import gradio as gr
10
+
11
+ from PIL import Image
12
+ from diffusers import StableDiffusionPipeline, StableDiffusionImg2ImgPipeline
13
+
14
 
15
  # =========================
16
  # DEVICE
 
18
  device = "cpu"
19
 
20
  # =========================
21
+ # DREAMSHAPER MODEL CONFIG
22
  # =========================
23
  MODEL_ID = "Lykon/dreamshaper-8"
24
 
25
+ # =========================
26
+ # GROQ API CONFIG
27
+ # =========================
28
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY", "")
29
+ GROQ_MODEL = os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
30
+ GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions"
31
+
32
  # =========================
33
  # GLOBAL STATE
34
  # =========================
 
36
  current_mode = None
37
  history = []
38
 
39
+
40
  # =========================
41
  # STYLE PRESETS
42
  # =========================
43
  STYLE_PRESETS = {
44
  "None": "",
45
+ "Realistic": "photorealistic, realistic lighting, realistic details, natural skin texture",
46
+ "Anime": "anime style, vibrant colors, clean line art, detailed anime illustration",
47
+ "Cinematic": "cinematic lighting, dramatic shadows, film still, movie composition",
48
+ "Fantasy": "fantasy art, magical atmosphere, epic composition, highly detailed",
49
+ "Product": "professional product photography, studio lighting, clean background",
50
  }
51
 
52
+
53
  # =========================
54
  # NEGATIVE PROMPT
55
  # =========================
56
  NEGATIVE_PROMPT = (
57
  "worst quality, low quality, blurry, bad anatomy, bad proportions, "
58
+ "extra limbs, missing limbs, distorted face, deformed hands, bad hands, "
59
+ "extra fingers, missing fingers, duplicated body parts, ugly, watermark, "
60
+ "text, logo, signature, jpeg artifacts, oversaturated"
61
  )
62
 
63
+
64
  # =========================
65
+ # SIMPLE LOCAL TRANSLATOR ID TO EN
66
+ # FALLBACK JIKA GROQ API TIDAK AKTIF
67
  # =========================
68
  def translate_prompt(prompt):
69
  if not prompt:
 
71
 
72
  mapping = {
73
  "pria": "man",
74
+ "laki-laki": "man",
75
  "wanita": "woman",
76
+ "perempuan": "woman",
77
  "anak": "child",
78
+ "wajah": "face",
79
+ "potret": "portrait",
80
  "gunung": "mountain",
81
  "pantai": "beach",
82
  "kota": "city",
83
  "malam": "night",
84
  "siang": "day",
85
+ "pagi": "morning",
86
+ "sore": "afternoon",
87
+ "senja": "sunset",
88
  "laut": "sea",
89
  "langit": "sky",
90
  "hutan": "forest",
91
  "mobil": "car",
92
+ "motor": "motorcycle",
93
  "rumah": "house",
94
  "jalan": "street",
95
  "sungai": "river",
 
99
  "burung": "bird",
100
  "bunga": "flower",
101
  "pohon": "tree",
102
+ "robot": "robot",
103
+ "hujan": "rain",
104
+ "salju": "snow",
105
+ "api": "fire",
106
+ "air": "water",
107
+ "cantik": "beautiful",
108
+ "tampan": "handsome",
109
+ "futuristik": "futuristic",
110
+ "tradisional": "traditional",
111
+ "jawa": "javanese",
112
+ "indonesia": "indonesia",
113
+ "desa": "village",
114
+ "sawah": "rice field",
115
+ "kerajaan": "kingdom",
116
+ "istana": "palace",
117
+ "emas": "gold",
118
+ "perak": "silver",
119
+ "hitam": "black",
120
+ "putih": "white",
121
+ "merah": "red",
122
+ "biru": "blue",
123
+ "hijau": "green",
124
+ "kuning": "yellow",
125
  }
126
 
127
  prompt = prompt.lower()
 
140
 
141
  portrait_keywords = [
142
  "person", "man", "woman", "girl", "boy",
143
+ "face", "portrait", "child", "people"
144
  ]
145
 
146
  scene_keywords = [
147
  "mountain", "city", "forest", "beach", "sky",
148
+ "sea", "street", "river", "lake", "tree",
149
+ "village", "landscape", "room", "house",
150
+ "rice field", "palace"
151
  ]
152
 
153
  if any(k in p for k in portrait_keywords):
 
160
 
161
 
162
  # =========================
163
+ # LOCAL PROMPT ENGINE
164
  # =========================
165
+ def build_prompt_local(prompt, style):
166
+ """
167
+ Fallback prompt engine lokal jika Groq API tidak tersedia.
168
+ """
169
  prompt = translate_prompt(prompt)
170
  style_text = STYLE_PRESETS.get(style, "")
171
 
172
+ base = "masterpiece, best quality, ultra detailed, sharp focus, high detail"
173
+ detail = "intricate details, detailed texture, balanced composition"
174
 
175
  ptype = detect_type(prompt)
176
 
177
  if ptype == "portrait":
178
  extra = (
179
+ "detailed face, natural skin texture, expressive eyes, "
180
+ "soft lighting, portrait composition, depth of field, "
181
+ "realistic facial proportions"
182
  )
183
  elif ptype == "scene":
184
  extra = (
185
  "wide shot, cinematic composition, environmental detail, "
186
+ "atmospheric lighting, realistic perspective, immersive scene"
187
  )
188
  else:
189
  extra = (
190
+ "centered composition, studio lighting, clean background, "
191
+ "sharp object details, professional photography"
192
  )
193
 
194
  parts = [
 
202
  return ", ".join([p for p in parts if p])
203
 
204
 
205
+ # =========================
206
+ # GROQ PROMPT ENHANCER
207
+ # =========================
208
+ def enhance_prompt_with_groq(user_prompt, style):
209
+ """
210
+ Mengubah prompt Bahasa Indonesia menjadi prompt Bahasa Inggris
211
+ yang lebih optimal untuk DreamShaper / Stable Diffusion menggunakan Groq API.
212
+ """
213
+
214
+ if not user_prompt:
215
+ return ""
216
+
217
+ if not GROQ_API_KEY:
218
+ print("GROQ_API_KEY belum diset. Menggunakan prompt lokal.")
219
+ return build_prompt_local(user_prompt, style)
220
+
221
+ system_prompt = """
222
+ You are an expert prompt engineer for Stable Diffusion DreamShaper.
223
+
224
+ Your task:
225
+ - Convert Indonesian image prompts into high-quality English prompts.
226
+ - Preserve the user's exact visual intent.
227
+ - Do not change the main subject.
228
+ - Do not add unrelated objects.
229
+ - Add useful visual details only when helpful:
230
+ composition, lighting, atmosphere, camera angle, lens, texture, detail level, realism, style.
231
+ - Make the final prompt suitable for Stable Diffusion / DreamShaper.
232
+ - Return only one final English prompt.
233
+ - Do not use markdown.
234
+ - Do not use bullet points.
235
+ - Do not explain anything.
236
+
237
+ Important:
238
+ - Keep the output concise but rich in visual detail.
239
+ - Avoid overly long prompts.
240
+ - Keep the prompt general-audience safe.
241
+ - If the request is unsafe or inappropriate, rewrite it into a safe, non-explicit, general-audience visual prompt.
242
+ """
243
+
244
+ selected_style = STYLE_PRESETS.get(style, "")
245
+
246
+ user_message = f"""
247
+ User Indonesian prompt:
248
+ {user_prompt}
249
+
250
+ Selected style:
251
+ {style}
252
+
253
+ Style keyword:
254
+ {selected_style}
255
+
256
+ Create one optimized English prompt for DreamShaper.
257
+ """
258
+
259
+ headers = {
260
+ "Authorization": f"Bearer {GROQ_API_KEY}",
261
+ "Content-Type": "application/json"
262
+ }
263
+
264
+ payload = {
265
+ "model": GROQ_MODEL,
266
+ "messages": [
267
+ {
268
+ "role": "system",
269
+ "content": system_prompt
270
+ },
271
+ {
272
+ "role": "user",
273
+ "content": user_message
274
+ }
275
+ ],
276
+ "temperature": 0.35,
277
+ "max_tokens": 300
278
+ }
279
+
280
+ try:
281
+ response = requests.post(
282
+ GROQ_API_URL,
283
+ headers=headers,
284
+ json=payload,
285
+ timeout=30
286
+ )
287
+
288
+ response.raise_for_status()
289
+
290
+ data = response.json()
291
+
292
+ enhanced_prompt = data["choices"][0]["message"]["content"].strip()
293
+ enhanced_prompt = enhanced_prompt.replace("\n", " ").strip()
294
+
295
+ if not enhanced_prompt:
296
+ return build_prompt_local(user_prompt, style)
297
+
298
+ return enhanced_prompt
299
+
300
+ except Exception as e:
301
+ print("Groq prompt enhancer error:", e)
302
+ print("Fallback ke prompt lokal.")
303
+ return build_prompt_local(user_prompt, style)
304
+
305
+
306
+ # =========================
307
+ # MAIN PROMPT BUILDER
308
+ # =========================
309
+ def build_prompt(prompt, style, use_groq=True):
310
+ """
311
+ Prompt utama.
312
+ Jika use_groq aktif dan API key tersedia, prompt diproses Groq.
313
+ Jika gagal, fallback ke prompt lokal.
314
+ """
315
+ if use_groq:
316
+ return enhance_prompt_with_groq(prompt, style)
317
+
318
+ return build_prompt_local(prompt, style)
319
+
320
+
321
  # =========================
322
  # LOAD PIPELINE DREAMSHAPER ONLY
323
  # =========================
 
451
  def generate_text_to_image(
452
  prompt,
453
  style,
454
+ use_groq,
455
  steps,
456
  width,
457
  height,
 
460
  ):
461
  pipe = load_pipe("txt2img")
462
 
463
+ final_prompt = build_prompt(prompt, style, use_groq)
464
+
465
+ print("FINAL TXT2IMG PROMPT:", final_prompt)
466
 
467
  width = normalize_size(width)
468
  height = normalize_size(height)
 
487
 
488
  file_path = save_image(image)
489
 
490
+ return image, file_path, seed, final_prompt, history
491
 
492
 
493
  # =========================
 
496
  def generate_image_to_image(
497
  prompt,
498
  style,
499
+ use_groq,
500
  steps,
501
  seed,
502
  input_image,
 
504
  up
505
  ):
506
  if input_image is None:
507
+ return None, None, None, "Input image belum diisi.", history
508
 
509
  pipe = load_pipe("img2img")
510
 
511
+ final_prompt = build_prompt(prompt, style, use_groq)
512
+
513
+ print("FINAL IMG2IMG PROMPT:", final_prompt)
514
 
515
  seed = normalize_seed(seed)
516
  input_image = prepare_img2img_input(input_image)
 
534
 
535
  file_path = save_image(image)
536
 
537
+ return image, file_path, seed, final_prompt, history
538
 
539
 
540
  # =========================
 
543
  def run_txt2img(
544
  prompt,
545
  style,
546
+ use_groq,
547
  steps,
548
  width,
549
  height,
 
555
  return generate_text_to_image(
556
  prompt,
557
  style,
558
+ use_groq,
559
  steps,
560
  width,
561
  height,
 
567
  gc.collect()
568
  time.sleep(2)
569
 
570
+ return None, None, None, "Generation failed. Check Space logs.", history
571
 
572
 
573
  def run_img2img(
574
  prompt,
575
  style,
576
+ use_groq,
577
  steps,
578
  seed,
579
  input_image,
 
585
  return generate_image_to_image(
586
  prompt,
587
  style,
588
+ use_groq,
589
  steps,
590
  seed,
591
  input_image,
 
597
  gc.collect()
598
  time.sleep(2)
599
 
600
+ return None, None, None, "Generation failed. Check Space logs.", history
601
 
602
 
603
  # =========================
 
635
  return [], None, None
636
 
637
 
638
+ def check_groq_status():
639
+ if GROQ_API_KEY:
640
+ return f"✅ Groq API aktif. Model: `{GROQ_MODEL}`"
641
+
642
+ return (
643
+ "⚠️ `GROQ_API_KEY` belum ditemukan. "
644
+ "Prompt enhancer akan memakai fallback lokal."
645
+ )
646
+
647
+
648
  # =========================
649
  # UI
650
  # =========================
651
  with gr.Blocks() as demo:
652
+ gr.Markdown("# 🚀 AI Image Studio - DreamShaper + Groq Prompt Enhancer")
653
+
654
  gr.Markdown(
655
  """
656
+ Masukkan prompt dalam **Bahasa Indonesia**.
657
+ Jika **Groq Prompt Enhancer** aktif, prompt akan diubah otomatis menjadi prompt Bahasa Inggris yang lebih optimal untuk DreamShaper.
 
 
 
 
 
658
  """
659
  )
660
 
661
+ gr.Markdown(check_groq_status())
662
+
663
  selected_history_index = gr.State(value=None)
664
 
665
  with gr.Tabs(selected="txt2img") as tabs:
 
670
  with gr.Tab("Text to Image", id="txt2img"):
671
  txt_prompt = gr.Textbox(
672
  lines=3,
673
+ label="Prompt Bahasa Indonesia",
674
+ placeholder="Contoh: pria memakai jaket hitam berdiri di jalan kota saat hujan malam hari"
675
  )
676
 
677
  txt_style = gr.Dropdown(
 
680
  label="Style"
681
  )
682
 
683
+ txt_use_groq = gr.Checkbox(
684
+ label="Gunakan Groq Prompt Enhancer",
685
+ value=True
686
+ )
687
+
688
  txt_steps = gr.Slider(
689
  minimum=10,
690
  maximum=30,
 
729
  label="Seed Used"
730
  )
731
 
732
+ txt_final_prompt = gr.Textbox(
733
+ lines=5,
734
+ label="Final Prompt ke DreamShaper",
735
+ interactive=False
736
+ )
737
+
738
  txt_file_output = gr.File(
739
  label="Download Image"
740
  )
 
745
  with gr.Tab("Image to Image", id="img2img"):
746
  img_prompt = gr.Textbox(
747
  lines=3,
748
+ label="Prompt Bahasa Indonesia",
749
+ placeholder="Contoh: ubah menjadi cinematic style, lighting dramatis, detail lebih realistis"
750
  )
751
 
752
  img_input = gr.Image(
 
754
  label="Input Image"
755
  )
756
 
757
+ img_style = gr.Dropdown(
758
+ choices=list(STYLE_PRESETS.keys()),
759
+ value="Realistic",
760
+ label="Style"
761
+ )
762
+
763
+ img_use_groq = gr.Checkbox(
764
+ label="Gunakan Groq Prompt Enhancer",
765
+ value=True
766
+ )
767
+
768
  img_strength = gr.Slider(
769
  minimum=0.1,
770
  maximum=1.0,
 
773
  label="Strength"
774
  )
775
 
 
 
 
 
 
 
776
  img_steps = gr.Slider(
777
  minimum=10,
778
  maximum=30,
 
801
  label="Seed Used"
802
  )
803
 
804
+ img_final_prompt = gr.Textbox(
805
+ lines=5,
806
+ label="Final Prompt ke DreamShaper",
807
+ interactive=False
808
+ )
809
+
810
  img_file_output = gr.File(
811
  label="Download Image"
812
  )
 
817
  with gr.Tab("History", id="history"):
818
  gr.Markdown(
819
  """
820
+ Gambar yang dibuat dari **Text to Image** atau **Image to Image**
821
+ otomatis muncul di sini.
822
 
823
  Cara memakai ulang:
824
  1. Klik salah satu gambar di History
 
849
  # EVENTS
850
  # =========================
851
 
 
852
  txt_button.click(
853
  fn=run_txt2img,
854
  inputs=[
855
  txt_prompt,
856
  txt_style,
857
+ txt_use_groq,
858
  txt_steps,
859
  txt_width,
860
  txt_height,
 
865
  txt_result,
866
  txt_file_output,
867
  txt_seed_output,
868
+ txt_final_prompt,
869
  history_gallery
870
  ]
871
  )
872
 
 
873
  img_button.click(
874
  fn=run_img2img,
875
  inputs=[
876
  img_prompt,
877
  img_style,
878
+ img_use_groq,
879
  img_steps,
880
  img_seed,
881
  img_input,
 
886
  img_result,
887
  img_file_output,
888
  img_seed_output,
889
+ img_final_prompt,
890
  history_gallery
891
  ]
892
  )
893
 
 
894
  history_gallery.select(
895
  fn=select_history,
896
  outputs=[
 
899
  ]
900
  )
901
 
 
902
  reuse_button.click(
903
  fn=reuse_history,
904
  inputs=[
 
910
  ]
911
  )
912
 
 
913
  clear_history_button.click(
914
  fn=clear_history,
915
  inputs=[],
 
920
  ]
921
  )
922
 
 
923
  demo.load(
924
  fn=lambda: history,
925
  inputs=[],