ssboost commited on
Commit
9a3407f
ยท
verified ยท
1 Parent(s): d46f527

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +393 -207
app.py CHANGED
@@ -10,8 +10,10 @@ import numpy as np
10
  from io import BytesIO
11
  from datetime import datetime, timedelta
12
  import random
 
 
 
13
  from dotenv import load_dotenv
14
- from gradio_client import Client, handle_file
15
 
16
  load_dotenv()
17
 
@@ -19,181 +21,340 @@ load_dotenv()
19
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
20
  logger = logging.getLogger(__name__)
21
 
22
- # API ์—”๋“œํฌ์ธํŠธ ์„ค์ • (ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ๋งŒ ์„ค์ •)
23
- API_ENDPOINT = os.environ.get("API_ENDPOINT", "")
24
-
25
- if not API_ENDPOINT:
26
- raise ValueError("API_ENDPOINT ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
27
-
28
- # ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
29
- try:
30
- client = Client(API_ENDPOINT)
31
- logger.info("ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ")
32
- except Exception as e:
33
- logger.error(f"ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์‹คํŒจ: {e}")
34
- raise
35
-
36
- def safe_client_call(api_name, **kwargs):
37
- """์•ˆ์ „ํ•œ ํด๋ผ์ด์–ธํŠธ ํ˜ธ์ถœ ํ•จ์ˆ˜"""
38
- try:
39
- return client.predict(api_name=api_name, **kwargs)
40
- except Exception as e:
41
- logger.error(f"API ํ˜ธ์ถœ ์‹คํŒจ ({api_name}): {e}")
42
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- def convert_image_to_file(image):
45
- """PIL Image ๊ฐ์ฒด๋ฅผ ์ž„์‹œ ํŒŒ์ผ๋กœ ๋ณ€ํ™˜"""
46
- if image is None:
47
- return None
48
 
49
- try:
50
- # PIL Image ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ
51
- if hasattr(image, 'save'):
52
- # ์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ
53
- temp_file = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
54
- image.save(temp_file.name, format='PNG')
55
- temp_file.close()
56
- return temp_file.name
57
- # ์ด๋ฏธ ํŒŒ์ผ ๊ฒฝ๋กœ์ธ ๊ฒฝ์šฐ
58
- elif isinstance(image, str):
59
- return image
60
- # ๊ธฐํƒ€ ๊ฒฝ์šฐ
61
- else:
62
- return image
63
- except Exception as e:
64
- logger.error(f"์ด๋ฏธ์ง€ ํŒŒ์ผ ๋ณ€ํ™˜ ์‹คํŒจ: {e}")
65
  return None
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- # ========== ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ ๊ด€๋ จ ํ•จ์ˆ˜ (ํด๋ผ์ด์–ธํŠธ API ํ™œ์šฉ) ==========
68
- def generate_single_image(image1, image2, image3, prompt):
69
- """๋‹จ์ผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ - ์›๋ณธ ํ•จ์ˆ˜๋ช… ์œ ์ง€"""
70
- try:
71
- # ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ฒ˜๋ฆฌ - PIL Image๋ฅผ ์ž„์‹œ ํŒŒ์ผ๋กœ ๋ณ€ํ™˜
72
- image1_path = convert_image_to_file(image1)
73
- image2_path = convert_image_to_file(image2)
74
- image3_path = convert_image_to_file(image3)
75
-
76
- # handle_file๋กœ ์ฒ˜๋ฆฌ
77
- image1_file = handle_file(image1_path) if image1_path else None
78
- image2_file = handle_file(image2_path) if image2_path else None
79
- image3_file = handle_file(image3_path) if image3_path else None
80
-
81
- result = safe_client_call(
82
- "/generate_single_image",
83
- image1=image1_file,
84
- image2=image2_file,
85
- image3=image3_file,
86
- prompt=prompt
87
- )
88
-
89
- if result:
90
- return result[0], result[1], result[2]
91
- else:
92
- return None, "API ํ˜ธ์ถœ ์‹คํŒจ", ""
93
-
94
- except Exception as e:
95
- logger.error(f"๋‹จ์ผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹คํŒจ: {e}")
96
- return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", ""
97
 
98
- def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progress()):
99
- """๋‹ค์ค‘ ์ด๋ฏธ์ง€ ์ƒ์„ฑ - ์›๋ณธ ํ•จ์ˆ˜๋ช… ์œ ์ง€"""
 
 
 
 
 
 
100
  try:
101
- progress(0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค€๋น„ ์ค‘...")
102
-
103
- # ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ฒ˜๋ฆฌ - PIL Image๋ฅผ ์ž„์‹œ ํŒŒ์ผ๋กœ ๋ณ€ํ™˜
104
- image1_path = convert_image_to_file(image1)
105
- image2_path = convert_image_to_file(image2)
106
- image3_path = convert_image_to_file(image3)
 
 
 
 
 
107
 
108
- # handle_file๋กœ ์ฒ˜๋ฆฌ
109
- image1_file = handle_file(image1_path) if image1_path else None
110
- image2_file = handle_file(image2_path) if image2_path else None
111
- image3_file = handle_file(image3_path) if image3_path else None
112
 
113
- progress(0.5, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘...")
 
 
114
 
115
- result = safe_client_call(
116
- "/generate_multiple_images",
117
- image1=image1_file,
118
- image2=image2_file,
119
- image3=image3_file,
120
- prompt=prompt
 
 
 
 
 
121
  )
122
 
123
- progress(1.0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ!")
 
 
 
124
 
125
- if result:
126
- return result[0], result[1], result[2], result[3]
 
 
 
 
127
  else:
128
- return None, None, "API ํ˜ธ์ถœ ์‹คํŒจ", ""
129
-
 
 
 
130
  except Exception as e:
131
- logger.error(f"๋‹ค์ค‘ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹คํŒจ: {e}")
132
- return None, None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", ""
133
-
134
- # ========== ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ ํ•จ์ˆ˜๋“ค - ํด๋ผ์ด์–ธํŠธ API ํ™œ์šฉ ==========
135
- def get_prompt_template_1():
136
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 1 - ๋ถ€๋ถ„๋ณ€๊ฒฝ-1"""
137
- return "(#1์˜ ์—ฌ์„ฑ)์ด ์‚ด์ง ๋’ค๋กœ ๋Œ์•„๋ณด๋Š” ๋ชจ์Šต์œผ๋กœ ์ตœ๋Œ€ํ•œ ์ด์ „ seed๋ฅผ ์œ ์ง€ํ•œํ…Œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ผ."
138
-
139
- def get_prompt_template_2():
140
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 2 - ๋ถ€๋ถ„๋ณ€๊ฒฝ-2"""
141
- return "(#1 ๋ ˆ๋ชจ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋งŒ ๊ฒ€์€์ƒ‰ ๊ณ ๋ž˜๋ ˆ๊ณ ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ seed๋ฅผ ๋ณ€๊ฒฝ์„ ํ•˜์ง€๋งˆ๋ผ."
142
-
143
- def get_prompt_template_3():
144
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 3 - ๋ถ€๋ถ„๋ณ€๊ฒฝ-3"""
145
- return "(#1 ์—ฌํ–‰์šฉ ์–ผ์Œ๋ฐ•์Šค)์•ž์— ์–ผ์Œ์ด ๋‹ด๊ธด 3์ž”์˜ ์ฝœ๋ผ๊ฐ€ ๋†“์—ฌ์žˆ๋Š” ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
- def get_prompt_template_4():
148
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 4 - ๊ธ€์ž์ง€์šฐ๊ธฐ"""
149
- return "(#1 ์ด๋ฏธ์ง€)์— ์žˆ๋Š” ์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜๋ผ."
 
 
 
 
150
 
151
- def get_prompt_template_5():
152
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 5 - ๊ธ€์ž๋ณ€๊ฒฝ"""
153
- return '(#1์˜ ํ…์ŠคํŠธ)๋ฅผ ์Šคํƒ€์ผ์„ ์œ ์ง€ํ•œ์ฒด ํ…์ŠคํŠธ๋งŒ "Hello"๋กœ ๋ฐ”๊ฟ”๋ผ'
154
 
155
- def get_prompt_template_6():
156
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 6 - ์ƒํ’ˆ์ฐฉ์šฉ-1"""
157
- return "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์œ ์ง€ํ•œ ์ฒด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์™€ (#3์˜ ์ฒญ๋ฐ”์ง€)๋ฅผ ์ง์ ‘ ๋ชจ๋ธ์ด ์ฐฉ์šฉํ•œ๊ฒƒ ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ."
158
 
159
- def get_prompt_template_7():
160
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 7 - ์ƒํ’ˆ์ฐฉ์šฉ-2"""
161
- return "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์„ ์ฐฉ์šฉํ•˜๊ณ  (#3์˜ ๋’ท๋ฐฐ๊ฒฝ์˜ ์นดํŽ˜์ „์ฒด๊ฐ€ ๋ณด์ด๋ฉฐ) ์˜์ž์— ์•‰์•„ ์žˆ๋Š” ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ."
162
 
163
- def get_prompt_template_8():
164
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 8 - ์ƒํ’ˆ๋“ค๊ณ """
165
- return "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด(#2์˜ ์™€์ธ์ž”)์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ."
166
 
167
- def get_prompt_template_9():
168
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 9 - ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ"""
169
- return "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2 ์นดํŽ˜)์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์žˆ๋Š” ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ."
 
 
170
 
171
- def get_prompt_template_10():
172
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 10 - ๋ถ€๋ถ„์ง€์šฐ๊ธฐ"""
173
- return "(#1์˜ ๋ ˆ๊ณ ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋ฅผ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์˜ ์ฃผ์š” ์š”์†Œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ ํ•ด์•ผํ•œ๋‹ค."
174
 
175
- def get_prompt_template_11():
176
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 11 - ์ด๋ฏธ์ง€ํ™•์žฅ"""
177
- return "(#1 ์ด๋ฏธ์ง€)๋ฅผ ์›๋ณธ๊ทธ๋Œ€๋กœ ์ค‘์•™์— ๋‘๊ณ  ๋น„์œจ๋กœ ์œ ์ง€ํ•œ ์ฒด ์œ„์•„๋ž˜ ๋ฐ ์ขŒ์šฐ๋กœ ํฌ๊ฒŒ ํ™•์žฅํ•˜๋ผ."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
- def get_prompt_template_12():
180
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 12 - ํ”Œ๋ ˆ์ดํŒ…-1"""
181
- return "(#1์…€๋Ÿฌ๋“œ)์— ๋‹ด์€ ์šฉ๊ธฐ๋Š” ๋ฒ„๋ฆฌ๊ณ  ๋„“๊ณ  ํฐ ์˜ˆ์œ ์ ‘์‹œ์— (#1์…€๋Ÿฌ๋“œ)์Œ์‹๋งŒ ๊ฐ€๋“ ์ฑ„์›Œ์„œ ์ƒ์—…์ ์ธ ๊ฐ๋„๋กœ ์–ด์šธ๋ฆฌ๋Š” ์†Œํ’ˆ๊ณผ ํ•จ๊ป˜ ํ”Œ๋ ˆ์ดํŒ… ํ•œ ๋ชจ์Šต์„ ์ด๋ฏธ์ง€๋กœ ์ƒ์„ฑํ•˜๋ผ. "
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
- def get_prompt_template_13():
184
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 13 - ํ”Œ๋ ˆ์ดํŒ…-2"""
185
- return "(#2 ํ”Œ๋ ˆ์ดํŒ…ํ•œ ์ด๋ฏธ์ง€)์— ๋‹ด๊ธด ์Œ์‹์„ (#1 ์ƒ๋Ÿฌ๋“œ)๋กœ ๋ฐ”๊พธ๊ณ  ๋‚˜๋จธ์ง€๋Š” ์‹œ๋“œ๋ฅผ ์œ ์ง€ํ•œ ์ฒด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
- def get_prompt_template_14():
188
- """ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ 14 - ํ”Œ๋ ˆ์ดํŒ…-3"""
189
- return "(#1์ปต)์— ๋”ธ๊ธฐ, ๋ฐ”๋‹๋ผ, ์ดˆ์ฝ” ์•„์ด์Šคํฌ๋ฆผ์„ ๋‹ด๊ณ  ๊ทธ ์œ„์— ์ดˆ์ฝ” ์‹œ๋Ÿฝ์ด ํ๋ฅด๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
  # ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ์ ์šฉ ์ปค์Šคํ…€ CSS ์Šคํƒ€์ผ
192
  custom_css = """
193
  /* ============================================
194
  ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋ณ€๊ฒฝ ํ…œํ”Œ๋ฆฟ CSS
195
  ============================================ */
196
-
197
  /* 1. CSS ๋ณ€์ˆ˜ ์ •์˜ (๋ผ์ดํŠธ๋ชจ๋“œ - ๊ธฐ๋ณธ๊ฐ’) */
198
  :root {
199
  /* ๋ฉ”์ธ ์ปฌ๋Ÿฌ */
@@ -225,7 +386,6 @@ custom_css = """
225
  /* ๊ธฐํƒ€ */
226
  --border-radius: 18px;
227
  }
228
-
229
  /* 2. ๋‹คํฌ๋ชจ๋“œ ์ƒ‰์ƒ ๋ณ€์ˆ˜ (์ž๋™ ๊ฐ์ง€) */
230
  @media (prefers-color-scheme: dark) {
231
  :root {
@@ -251,7 +411,6 @@ custom_css = """
251
  --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
252
  }
253
  }
254
-
255
  /* 3. ์ˆ˜๋™ ๋‹คํฌ๋ชจ๋“œ ํด๋ž˜์Šค (Gradio ํ† ๊ธ€์šฉ) */
256
  [data-theme="dark"],
257
  .dark,
@@ -277,7 +436,6 @@ custom_css = """
277
  --shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
278
  --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
279
  }
280
-
281
  /* 4. ๊ธฐ๋ณธ ์š”์†Œ ๋‹คํฌ๋ชจ๋“œ ์ ์šฉ */
282
  body {
283
  font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
@@ -286,7 +444,6 @@ body {
286
  line-height: 1.6;
287
  transition: background-color 0.3s ease, color 0.3s ease;
288
  }
289
-
290
  /* 5. Gradio ์ปจํ…Œ์ด๋„ˆ ๊ฐ•์ œ ์ ์šฉ */
291
  .gradio-container,
292
  .gradio-container *,
@@ -296,7 +453,6 @@ body {
296
  background-color: var(--background-color) !important;
297
  color: var(--text-color) !important;
298
  }
299
-
300
  /* Gradio ์ปจํ…Œ์ด๋„ˆ ์˜ค๋ฒ„๋ผ์ด๋“œ */
301
  .gradio-container {
302
  max-width: 100% !important;
@@ -306,20 +462,17 @@ body {
306
  background-color: var(--background-color) !important;
307
  box-sizing: border-box !important;
308
  }
309
-
310
  /* ์ถ”๊ฐ€: ๋‚ด๋ถ€ ์ปจํ…Œ์ด๋„ˆ๋„ 100% ๋„ˆ๋น„๋กœ ์„ค์ • */
311
  .contain {
312
  max-width: 100% !important;
313
  width: 100% !important;
314
  }
315
-
316
  /* ์ถ”๊ฐ€: ๊ฐ ํ–‰(Row)๋„ 100% ๋„ˆ๋น„๋กœ ์„ค์ • */
317
  .gr-padded {
318
  padding: 0 !important;
319
  width: 100% !important;
320
  max-width: 100% !important;
321
  }
322
-
323
  /* 6. ์นด๋“œ ๋ฐ ํŒจ๋„ ์Šคํƒ€์ผ */
324
  .gr-group,
325
  .gr-form,
@@ -338,11 +491,9 @@ body {
338
  transition: transform 0.3s ease, background-color 0.3s ease;
339
  color: var(--text-color) !important;
340
  }
341
-
342
  .gr-group:hover {
343
  transform: translateY(-5px);
344
  }
345
-
346
  /* 7. ์ž…๋ ฅ ํ•„๋“œ ์Šคํƒ€์ผ */
347
  input[type="text"],
348
  input[type="number"],
@@ -362,7 +513,6 @@ select,
362
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05) !important;
363
  transition: all 0.3s ease !important;
364
  }
365
-
366
  input[type="text"]:focus,
367
  input[type="number"]:focus,
368
  input[type="email"]:focus,
@@ -377,7 +527,6 @@ select:focus,
377
  outline: none !important;
378
  box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important;
379
  }
380
-
381
  /* 8. ๋ผ๋ฒจ ๋ฐ ํ…์ŠคํŠธ ์š”์†Œ */
382
  label,
383
  .gr-label,
@@ -386,7 +535,6 @@ label,
386
  p, span, div {
387
  color: var(--text-color) !important;
388
  }
389
-
390
  /* 9. ์„น์…˜ ์ œ๋ชฉ */
391
  .section-title {
392
  font-size: 22px !important;
@@ -398,11 +546,9 @@ p, span, div {
398
  display: flex;
399
  align-items: center;
400
  }
401
-
402
  .section-title span {
403
  color: var(--primary-color);
404
  }
405
-
406
  /* 10. ๋ฒ„ํŠผ ์Šคํƒ€์ผ๋ง */
407
  .custom-button {
408
  background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important;
@@ -418,16 +564,13 @@ p, span, div {
418
  align-items: center !important;
419
  justify-content: center !important;
420
  }
421
-
422
  .custom-button:hover {
423
  transform: translateY(-2px) !important;
424
  box-shadow: 0 6px 12px rgba(251, 127, 13, 0.3) !important;
425
  }
426
-
427
  .custom-button.primary {
428
  background: linear-gradient(135deg, var(--accent-color), #ff9a8b) !important;
429
  }
430
-
431
  button:not([class*="custom"]):not([class*="primary"]):not([class*="secondary"]) {
432
  background-color: var(--card-bg) !important;
433
  color: var(--text-color) !important;
@@ -435,7 +578,6 @@ button:not([class*="custom"]):not([class*="primary"]):not([class*="secondary"])
435
  border-radius: var(--border-radius) !important;
436
  transition: all 0.3s ease !important;
437
  }
438
-
439
  /* 11. ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ */
440
  .image-container {
441
  border-radius: var(--border-radius);
@@ -444,44 +586,36 @@ button:not([class*="custom"]):not([class*="primary"]):not([class*="secondary"])
444
  transition: all 0.3s ease;
445
  background-color: var(--card-bg);
446
  }
447
-
448
  .image-container:hover {
449
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
450
  }
451
-
452
  /* 12. ํ…Œ์ด๋ธ” ์Šคํƒ€์ผ */
453
  table {
454
  background-color: var(--card-bg) !important;
455
  color: var(--text-color) !important;
456
  border-color: var(--border-color) !important;
457
  }
458
-
459
  table th {
460
  background-color: var(--primary-color) !important;
461
  color: white !important;
462
  border-color: var(--border-color) !important;
463
  }
464
-
465
  table td {
466
  background-color: var(--card-bg) !important;
467
  color: var(--text-color) !important;
468
  border-color: var(--border-color) !important;
469
  }
470
-
471
  table tbody tr:nth-child(even) {
472
  background-color: var(--table-even-bg) !important;
473
  }
474
-
475
  table tbody tr:hover {
476
  background-color: var(--table-hover-bg) !important;
477
  }
478
-
479
  /* 13. ์ฒดํฌ๋ฐ•์Šค ๋ฐ ๋ผ๋””์˜ค ๋ฒ„ํŠผ */
480
  input[type="checkbox"],
481
  input[type="radio"] {
482
  accent-color: var(--primary-color) !important;
483
  }
484
-
485
  /* 14. ๋ฒ„ํŠผ ๊ทธ๋ฃน */
486
  .button-grid {
487
  display: grid;
@@ -489,24 +623,20 @@ input[type="radio"] {
489
  gap: 0.8rem;
490
  margin-bottom: 1.2rem;
491
  }
492
-
493
  /* 15. ์Šคํฌ๋กค๋ฐ” ์Šคํƒ€์ผ */
494
  ::-webkit-scrollbar-thumb:hover {
495
  background: var(--secondary-color);
496
  }
497
-
498
  /* 16. ์•„์ฝ”๋””์–ธ ๋ฐ ๋“œ๋กญ๋‹ค์šด */
499
  details {
500
  background-color: var(--card-bg) !important;
501
  border-color: var(--border-color) !important;
502
  color: var(--text-color) !important;
503
  }
504
-
505
  details summary {
506
  background-color: var(--card-bg) !important;
507
  color: var(--text-color) !important;
508
  }
509
-
510
  /* 17. ํˆดํŒ ๋ฐ ํŒ์—… */
511
  [data-tooltip]:hover::after,
512
  .tooltip,
@@ -516,7 +646,6 @@ details summary {
516
  border-color: var(--border-color) !important;
517
  box-shadow: var(--shadow-light) !important;
518
  }
519
-
520
  /* 18. ๋ชจ๋‹ฌ ๋ฐ ์˜ค๋ฒ„๋ ˆ์ด */
521
  .modal,
522
  .overlay,
@@ -526,7 +655,6 @@ details summary {
526
  color: var(--text-color) !important;
527
  border-color: var(--border-color) !important;
528
  }
529
-
530
  /* 19. ์ถ”๊ฐ€ Gradio ์ปดํฌ๋„ŒํŠธ๋“ค */
531
  .gr-block,
532
  .gr-group,
@@ -535,7 +663,6 @@ details summary {
535
  background-color: var(--background-color) !important;
536
  color: var(--text-color) !important;
537
  }
538
-
539
  /* 20. ์ฝ”๋“œ ๋ธ”๋ก ๋ฐ pre ํƒœ๊ทธ */
540
  code,
541
  pre,
@@ -544,7 +671,6 @@ pre,
544
  color: var(--text-color) !important;
545
  border-color: var(--border-color) !important;
546
  }
547
-
548
  /* 21. ์•Œ๋ฆผ ๋ฐ ๋ฉ”์‹œ์ง€ */
549
  .alert,
550
  .message,
@@ -556,17 +682,14 @@ pre,
556
  color: var(--text-color) !important;
557
  border-color: var(--border-color) !important;
558
  }
559
-
560
  /* 22. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šคํƒ€์ผ */
561
  @keyframes fadeIn {
562
  from { opacity: 0; transform: translateY(10px); }
563
  to { opacity: 1; transform: translateY(0); }
564
  }
565
-
566
  .fade-in {
567
  animation: fadeIn 0.5s ease-out;
568
  }
569
-
570
  /* 23. Examples ์„น์…˜ ์Šคํƒ€์ผ */
571
  .examples-section {
572
  display: grid;
@@ -574,7 +697,6 @@ pre,
574
  gap: 1.5rem;
575
  margin-top: 1rem;
576
  }
577
-
578
  .example-item {
579
  background-color: var(--card-bg);
580
  border-radius: var(--border-radius);
@@ -582,18 +704,15 @@ pre,
582
  box-shadow: var(--shadow);
583
  transition: transform 0.3s ease, background-color 0.3s ease;
584
  }
585
-
586
  .example-item:hover {
587
  transform: translateY(-5px);
588
  }
589
-
590
  /* 24. ์ „ํ™˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ */
591
  * {
592
  transition: background-color 0.3s ease,
593
  color 0.3s ease,
594
  border-color 0.3s ease !important;
595
  }
596
-
597
  /* 25. ๋ฐ˜์‘ํ˜• */
598
  @media (max-width: 768px) {
599
  .button-grid {
@@ -621,7 +740,10 @@ with gr.Blocks(css=custom_css, theme=gr.themes.Default(
621
  font=[gr.themes.GoogleFont("Noto Sans KR"), "ui-sans-serif", "system-ui"]
622
  )) as demo:
623
  gr.HTML(fontawesome_link)
 
 
624
 
 
625
  with gr.Row(equal_height=True):
626
  with gr.Column(scale=1):
627
  # ======== ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋ฐ ์„ค์ • ์„น์…˜ ========
@@ -710,36 +832,100 @@ with gr.Blocks(css=custom_css, theme=gr.themes.Default(
710
  )
711
 
712
  # ========== ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ ==========
713
- # ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ
714
- image_change_btn1.click(fn=get_prompt_template_1, outputs=prompt_input)
715
- image_change_btn2.click(fn=get_prompt_template_2, outputs=prompt_input)
716
- image_change_btn3.click(fn=get_prompt_template_3, outputs=prompt_input)
717
- text_remove_btn.click(fn=get_prompt_template_4, outputs=prompt_input)
718
- text_change_btn.click(fn=get_prompt_template_5, outputs=prompt_input)
719
- clothes_change_btn1.click(fn=get_prompt_template_6, outputs=prompt_input)
720
- clothes_change_btn2.click(fn=get_prompt_template_7, outputs=prompt_input)
721
- holding_product_btn.click(fn=get_prompt_template_8, outputs=prompt_input)
722
- background_change_btn.click(fn=get_prompt_template_9, outputs=prompt_input)
723
- composite_product_btn.click(fn=get_prompt_template_10, outputs=prompt_input)
724
- outpainting_btn.click(fn=get_prompt_template_11, outputs=prompt_input)
725
- food_btn_1.click(fn=get_prompt_template_12, outputs=prompt_input)
726
- food_btn_2.click(fn=get_prompt_template_13, outputs=prompt_input)
727
- food_btn_3.click(fn=get_prompt_template_14, outputs=prompt_input)
728
-
729
- # ๋‹จ์ผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
  submit_single_btn.click(
731
  fn=generate_single_image,
732
  inputs=[image1_input, image2_input, image3_input, prompt_input],
733
  outputs=[output_image1, output_text, prompt_display],
734
  )
735
 
736
- # 2์žฅ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
737
  submit_btn.click(
738
  fn=generate_multiple_images,
739
  inputs=[image1_input, image2_input, image3_input, prompt_input],
740
  outputs=[output_image1, output_image2, output_text, prompt_display],
741
  )
742
 
743
- # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
 
 
744
  demo.queue()
745
- demo.launch(share=False, inbrowser=True, width="100%")
 
10
  from io import BytesIO
11
  from datetime import datetime, timedelta
12
  import random
13
+
14
+ from google import genai
15
+ from google.genai import types
16
  from dotenv import load_dotenv
 
17
 
18
  load_dotenv()
19
 
 
21
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
22
  logger = logging.getLogger(__name__)
23
 
24
+ # ------------------- API ํ‚ค ์ˆœํ™˜ ์‹œ์Šคํ…œ -------------------
25
+ # API ํ‚ค ์„ค์ • - ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ API_KEYS = [...] ๊ตฌ๋ฌธ ๊ทธ๋Œ€๋กœ ์‹คํ–‰
26
+ def load_api_configs_from_env():
27
+ env_configs = os.getenv('API_KEYS')
28
+ if env_configs:
29
+ try:
30
+ # API_KEYS = [...] ๊ตฌ๋ฌธ์„ ๊ทธ๋Œ€๋กœ ์‹คํ–‰
31
+ exec(env_configs, globals())
32
+ return globals().get('API_KEYS', [])
33
+ except Exception as e:
34
+ logger.error(f"โš ๏ธ API_KEYS ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์‹คํ–‰ ์˜ค๋ฅ˜: {e}")
35
+ return []
36
+ return []
37
+
38
+ # ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๋กœ๋“œํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ
39
+ loaded_configs = load_api_configs_from_env()
40
+ API_KEYS = loaded_configs if loaded_configs else [
41
+ "",
42
+ "",
43
+ "",
44
+ "",
45
+ "",
46
+ "",
47
+ "",
48
+ "",
49
+ "",
50
+ "",
51
+ ]
52
+
53
+ current_key_index = -1 # -1์€ ์•„์ง ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์Œ์„ ์˜๋ฏธ
54
+
55
+ def initialize_api_keys():
56
+ """API ํ‚ค ๋ชฉ๋ก์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•จ์ˆ˜"""
57
+ global API_KEYS
58
+
59
+ # ๋นˆ ๋ฌธ์ž์—ด์ด๋‚˜ None์ธ ํ‚ค๋“ค ์ œ๊ฑฐ
60
+ API_KEYS = [key for key in API_KEYS if key and key.strip() and not key.startswith("your_")]
61
+
62
+ logger.info(f"API ํ‚ค {len(API_KEYS)}๊ฐœ๊ฐ€ ๋กœ๋“œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
63
+
64
+ if len(API_KEYS) == 0:
65
+ logger.error("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ API ํ‚ค๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. API_KEYS ๋ฆฌ์ŠคํŠธ์— ์‹ค์ œ ํ‚ค๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
66
 
67
+ def get_next_api_key():
68
+ """๋‹ค์Œ API ํ‚ค๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜ (์ฒ˜์Œ์—๋Š” ๋žœ๋ค, ์ดํ›„์—๋Š” ์ˆœ์ฐจ์ )"""
69
+ global current_key_index, API_KEYS
 
70
 
71
+ if not API_KEYS:
72
+ logger.error("API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  return None
74
+
75
+ if current_key_index == -1:
76
+ # ์ฒซ ๋ฒˆ์งธ ์‚ฌ์šฉ์€ ๋žœ๋ค์œผ๋กœ ์‹œ์ž‘์  ์„ ํƒ
77
+ current_key_index = random.randint(0, len(API_KEYS) - 1)
78
+ logger.info(f"์ฒซ ๋ฒˆ์งธ API ํ‚ค ๋žœ๋ค ์„ ํƒ: ์ธ๋ฑ์Šค {current_key_index}")
79
+ else:
80
+ # ์ดํ›„์—๋Š” ์ˆœ์ฐจ์ ์œผ๋กœ ๋‹ค์Œ ํ‚ค ์‚ฌ์šฉ
81
+ current_key_index = (current_key_index + 1) % len(API_KEYS)
82
+ logger.info(f"๋‹ค์Œ API ํ‚ค ์ˆœ์ฐจ ์„ ํƒ: ์ธ๋ฑ์Šค {current_key_index}")
83
+
84
+ return API_KEYS[current_key_index]
85
 
86
+ # ========== ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ ๊ด€๋ จ ํ•จ์ˆ˜ ==========
87
+ def save_binary_file(file_name, data):
88
+ with open(file_name, "wb") as f:
89
+ f.write(data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
+ def translate_prompt_to_english(prompt):
92
+ if not re.search("[๊ฐ€-ํžฃ]", prompt):
93
+ return prompt
94
+
95
+ prompt = prompt.replace("#1", "IMAGE_TAG_ONE")
96
+ prompt = prompt.replace("#2", "IMAGE_TAG_TWO")
97
+ prompt = prompt.replace("#3", "IMAGE_TAG_THREE")
98
+
99
  try:
100
+ api_key = get_next_api_key()
101
+ if not api_key:
102
+ logger.error("Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
103
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
104
+ prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
105
+ prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
106
+ return prompt
107
+
108
+ client = genai.Client(api_key=api_key)
109
+ translation_prompt = f"""
110
+ Translate the following Korean text to English:
111
 
112
+ {prompt}
 
 
 
113
 
114
+ IMPORTANT: The tokens IMAGE_TAG_ONE, IMAGE_TAG_TWO, and IMAGE_TAG_THREE are special tags
115
+ and must be preserved exactly as is in your translation. Do not translate these tokens.
116
+ """
117
 
118
+ logger.info(f"Translation prompt: {translation_prompt}")
119
+ response = client.models.generate_content(
120
+ model="gemini-2.0-flash",
121
+ contents=[translation_prompt],
122
+ config=types.GenerateContentConfig(
123
+ response_modalities=['Text'],
124
+ temperature=0.2,
125
+ top_p=0.95,
126
+ top_k=40,
127
+ max_output_tokens=512
128
+ )
129
  )
130
 
131
+ translated_text = ""
132
+ for part in response.candidates[0].content.parts:
133
+ if hasattr(part, 'text') and part.text:
134
+ translated_text += part.text
135
 
136
+ if translated_text.strip():
137
+ translated_text = translated_text.replace("IMAGE_TAG_ONE", "#1")
138
+ translated_text = translated_text.replace("IMAGE_TAG_TWO", "#2")
139
+ translated_text = translated_text.replace("IMAGE_TAG_THREE", "#3")
140
+ logger.info(f"Translated text: {translated_text.strip()}")
141
+ return translated_text.strip()
142
  else:
143
+ logger.warning("๋ฒˆ์—ญ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ")
144
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
145
+ prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
146
+ prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
147
+ return prompt
148
  except Exception as e:
149
+ logger.exception("๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
150
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
151
+ prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
152
+ prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
153
+ return prompt
154
+
155
+ def preprocess_prompt(prompt, image1, image2, image3):
156
+ # ๊ธฐ์กด ํ•จ์ˆ˜ ์œ ์ง€
157
+ has_img1 = image1 is not None
158
+ has_img2 = image2 is not None
159
+ has_img3 = image3 is not None
160
+
161
+ if "#1" in prompt and not has_img1:
162
+ prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
163
+ else:
164
+ prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
165
+
166
+ if "#2" in prompt and not has_img2:
167
+ prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
168
+ else:
169
+ prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
170
+
171
+ if "#3" in prompt and not has_img3:
172
+ prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
173
+ else:
174
+ prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
175
+
176
+ if "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ" in prompt:
177
+ desc_match = re.search(r'#1์„ "(.*?)"์œผ๋กœ ๋ฐ”๊ฟ”๋ผ', prompt)
178
+ if desc_match:
179
+ description = desc_match.group(1)
180
+ prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ {description}์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ๋‚ด์šฉ์€ ์œ ์ง€ํ•˜๋˜ ์ƒˆ๋กœ์šด ์Šคํƒ€์ผ๊ณผ ๋ถ„์œ„๊ธฐ๋กœ ์žฌํ•ด์„ํ•ด์ฃผ์„ธ์š”."
181
+ else:
182
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
183
 
184
+ elif "2. ๊ธ€์ž์ง€์šฐ๊ธฐ" in prompt:
185
+ text_match = re.search(r'#1์—์„œ "(.*?)"๋ฅผ ์ง€์›Œ๋ผ', prompt)
186
+ if text_match:
187
+ text_to_remove = text_match.group(1)
188
+ prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ '{text_to_remove}' ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ํ…์ŠคํŠธ๊ฐ€ ์žˆ๋˜ ๋ถ€๋ถ„์„ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ์ฑ„์›Œ์ฃผ์„ธ์š”."
189
+ else:
190
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ ๋ชจ๋“  ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ๊น”๋”ํ•œ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
191
 
192
+ elif "4. ์˜ท๋ฐ”๊พธ๊ธฐ" in prompt:
193
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ ์˜์ƒ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜์ƒ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์˜์ƒ์˜ ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋ฅด๋˜, ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
 
194
 
195
+ elif "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ" in prompt:
196
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”."
 
197
 
198
+ elif "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)" in prompt:
199
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์™€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(๋˜๋Š” ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€)๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ , ํŠนํžˆ ์ƒํ’ˆ์ด ๋‹๋ณด์ด๋„๋ก ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•ด์ฃผ์„ธ์š”."
 
200
 
201
+ prompt += " ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”. ์ด๋ฏธ์ง€์— ํ…์ŠคํŠธ๋‚˜ ๊ธ€์ž๋ฅผ ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”."
202
+ return prompt
 
203
 
204
+ def generate_with_images(prompt, images, variation_index=0):
205
+ try:
206
+ api_key = get_next_api_key()
207
+ if not api_key:
208
+ return None, "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. API_KEYS ํ™˜๊ฒฝ๋ณ€์ˆ˜์— API ํ‚ค๋ฅผ ์„ค์ •ํ•ด์ฃผ์„ธ์š”."
209
 
210
+ client = genai.Client(api_key=api_key)
211
+ logger.info(f"Gemini API ์š”์ฒญ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: {prompt}, ๋ณ€ํ˜• ์ธ๋ฑ์Šค: {variation_index}")
 
212
 
213
+ variation_suffixes = [
214
+ " Create this as the first variation. Do not add any text, watermarks, or labels to the image.",
215
+ " Create this as the second variation with more vivid colors. Do not add any text, watermarks, or labels to the image."
216
+ ]
217
+
218
+ if variation_index < len(variation_suffixes):
219
+ prompt = prompt + variation_suffixes[variation_index]
220
+ else:
221
+ prompt = prompt + " Do not add any text, watermarks, or labels to the image."
222
+
223
+ contents = [prompt]
224
+ for idx, img in enumerate(images, 1):
225
+ if img is not None:
226
+ contents.append(img)
227
+ logger.info(f"์ด๋ฏธ์ง€ #{idx} ์ถ”๊ฐ€๋จ")
228
+
229
+ response = client.models.generate_content(
230
+ model="gemini-2.0-flash-exp-image-generation",
231
+ contents=contents,
232
+ config=types.GenerateContentConfig(
233
+ response_modalities=['Text', 'Image'],
234
+ temperature=1,
235
+ top_p=0.95,
236
+ top_k=40,
237
+ max_output_tokens=8192
238
+ )
239
+ )
240
 
241
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
242
+ temp_path = tmp.name
243
+ result_text = ""
244
+ image_found = False
245
+ for part in response.candidates[0].content.parts:
246
+ if hasattr(part, 'text') and part.text:
247
+ result_text += part.text
248
+ logger.info(f"์‘๋‹ต ํ…์ŠคํŠธ: {part.text}")
249
+ elif hasattr(part, 'inline_data') and part.inline_data:
250
+ save_binary_file(temp_path, part.inline_data.data)
251
+ image_found = True
252
+ logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
253
+ if not image_found:
254
+ return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
255
+ result_img = Image.open(temp_path)
256
+ if result_img.mode == "RGBA":
257
+ result_img = result_img.convert("RGB")
258
+ return result_img, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
259
+ except Exception as e:
260
+ logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
261
+ return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
262
 
263
+ def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0, max_retries=3):
264
+ retry_count = 0
265
+ last_error = None
266
+
267
+ while retry_count < max_retries:
268
+ try:
269
+ images = [image1, image2, image3]
270
+ valid_images = [img for img in images if img is not None]
271
+
272
+ if not valid_images: # ์—ฌ๊ธฐ๊ฐ€ 254๋ฒˆ์งธ ์ค„ ๊ทผ์ฒ˜์ผ ๊ฒƒ
273
+ if prompt and prompt.strip():
274
+ # ํ”„๋กฌํ”„ํŠธ๋งŒ ์žˆ๋Š” ๊ฒฝ์šฐ ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ƒ์„ฑ ์‹œ๋„
275
+ logger.info("ํ…์ŠคํŠธ ํ”„๋กฌํ”„ํŠธ๋งŒ์œผ๋กœ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹œ๋„")
276
+ try:
277
+ # ๋นˆ ์ด๋ฏธ์ง€ ๋ฆฌ์ŠคํŠธ๋กœ ์ƒ์„ฑ ์‹œ๋„
278
+ result_img, status = generate_with_images(prompt, [], variation_index)
279
+ if result_img is not None:
280
+ return result_img, status, prompt
281
+ except:
282
+ pass
283
+ return None, "์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ฑฐ๋‚˜ ํ…์ŠคํŠธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", ""
284
+
285
+ if prompt and prompt.strip():
286
+ processed_prompt = preprocess_prompt(prompt, image1, image2, image3)
287
+ if re.search("[๊ฐ€-ํžฃ]", processed_prompt):
288
+ final_prompt = translate_prompt_to_english(processed_prompt)
289
+ else:
290
+ final_prompt = processed_prompt
291
+ else:
292
+ if len(valid_images) == 1:
293
+ final_prompt = "Please creatively transform this image into a more vivid and artistic version. Do not include any text or watermarks in the generated image."
294
+ logger.info("Default prompt generated for single image")
295
+ elif len(valid_images) == 2:
296
+ final_prompt = "Please seamlessly composite these two images, integrating their key elements harmoniously into a single image. Do not include any text or watermarks in the generated image."
297
+ logger.info("Default prompt generated for two images")
298
+ else:
299
+ final_prompt = "Please creatively composite these three images, combining their main elements into a cohesive and natural scene. Do not include any text or watermarks in the generated image."
300
+ logger.info("Default prompt generated for three images")
301
+
302
+ result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
303
+ if result_img is not None:
304
+ return result_img, status, final_prompt
305
+ else:
306
+ last_error = status
307
+ retry_count += 1
308
+ logger.warning(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹คํŒจ, ์žฌ์‹œ๋„ {retry_count}/{max_retries}: {status}")
309
+ time.sleep(1)
310
+ except Exception as e:
311
+ last_error = str(e)
312
+ retry_count += 1
313
+ logger.exception(f"์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ, ์žฌ์‹œ๋„ {retry_count}/{max_retries}:")
314
+ time.sleep(1)
315
+
316
+ return None, f"์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜({max_retries}ํšŒ) ์ดˆ๊ณผ ํ›„ ์‹คํŒจ: {last_error}", prompt
317
 
318
+ def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progress()):
319
+ results = []
320
+ statuses = []
321
+ prompts = []
322
+
323
+ num_images = 2
324
+ max_retries = 3
325
+
326
+ progress(0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค€๋น„ ์ค‘...")
327
+
328
+ for i in range(num_images):
329
+ progress((i / num_images), desc=f"{i+1}/{num_images} ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘...")
330
+ result_img, status, final_prompt = process_images_with_prompt(image1, image2, image3, prompt, i, max_retries)
331
+
332
+ if result_img is not None:
333
+ results.append(result_img)
334
+ statuses.append(f"์ด๋ฏธ์ง€ #{i+1}: {status}")
335
+ prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
336
+ else:
337
+ results.append(None)
338
+ statuses.append(f"์ด๋ฏธ์ง€ #{i+1} ์ƒ์„ฑ ์‹คํŒจ: {status}")
339
+ prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
340
+
341
+ time.sleep(1)
342
+
343
+ progress(1.0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ!")
344
+
345
+ while len(results) < 2:
346
+ results.append(None)
347
+
348
+ combined_status = "\n".join(statuses)
349
+ combined_prompts = "\n".join(prompts)
350
+
351
+ return results[0], results[1], combined_status, combined_prompts
352
 
353
  # ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ์ ์šฉ ์ปค์Šคํ…€ CSS ์Šคํƒ€์ผ
354
  custom_css = """
355
  /* ============================================
356
  ๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋ณ€๊ฒฝ ํ…œํ”Œ๋ฆฟ CSS
357
  ============================================ */
 
358
  /* 1. CSS ๋ณ€์ˆ˜ ์ •์˜ (๋ผ์ดํŠธ๋ชจ๋“œ - ๊ธฐ๋ณธ๊ฐ’) */
359
  :root {
360
  /* ๋ฉ”์ธ ์ปฌ๋Ÿฌ */
 
386
  /* ๊ธฐํƒ€ */
387
  --border-radius: 18px;
388
  }
 
389
  /* 2. ๋‹คํฌ๋ชจ๋“œ ์ƒ‰์ƒ ๋ณ€์ˆ˜ (์ž๋™ ๊ฐ์ง€) */
390
  @media (prefers-color-scheme: dark) {
391
  :root {
 
411
  --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
412
  }
413
  }
 
414
  /* 3. ์ˆ˜๋™ ๋‹คํฌ๋ชจ๋“œ ํด๋ž˜์Šค (Gradio ํ† ๊ธ€์šฉ) */
415
  [data-theme="dark"],
416
  .dark,
 
436
  --shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
437
  --shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
438
  }
 
439
  /* 4. ๊ธฐ๋ณธ ์š”์†Œ ๋‹คํฌ๋ชจ๋“œ ์ ์šฉ */
440
  body {
441
  font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
 
444
  line-height: 1.6;
445
  transition: background-color 0.3s ease, color 0.3s ease;
446
  }
 
447
  /* 5. Gradio ์ปจํ…Œ์ด๋„ˆ ๊ฐ•์ œ ์ ์šฉ */
448
  .gradio-container,
449
  .gradio-container *,
 
453
  background-color: var(--background-color) !important;
454
  color: var(--text-color) !important;
455
  }
 
456
  /* Gradio ์ปจํ…Œ์ด๋„ˆ ์˜ค๋ฒ„๋ผ์ด๋“œ */
457
  .gradio-container {
458
  max-width: 100% !important;
 
462
  background-color: var(--background-color) !important;
463
  box-sizing: border-box !important;
464
  }
 
465
  /* ์ถ”๊ฐ€: ๋‚ด๋ถ€ ์ปจํ…Œ์ด๋„ˆ๋„ 100% ๋„ˆ๋น„๋กœ ์„ค์ • */
466
  .contain {
467
  max-width: 100% !important;
468
  width: 100% !important;
469
  }
 
470
  /* ์ถ”๊ฐ€: ๊ฐ ํ–‰(Row)๋„ 100% ๋„ˆ๋น„๋กœ ์„ค์ • */
471
  .gr-padded {
472
  padding: 0 !important;
473
  width: 100% !important;
474
  max-width: 100% !important;
475
  }
 
476
  /* 6. ์นด๋“œ ๋ฐ ํŒจ๋„ ์Šคํƒ€์ผ */
477
  .gr-group,
478
  .gr-form,
 
491
  transition: transform 0.3s ease, background-color 0.3s ease;
492
  color: var(--text-color) !important;
493
  }
 
494
  .gr-group:hover {
495
  transform: translateY(-5px);
496
  }
 
497
  /* 7. ์ž…๋ ฅ ํ•„๋“œ ์Šคํƒ€์ผ */
498
  input[type="text"],
499
  input[type="number"],
 
513
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05) !important;
514
  transition: all 0.3s ease !important;
515
  }
 
516
  input[type="text"]:focus,
517
  input[type="number"]:focus,
518
  input[type="email"]:focus,
 
527
  outline: none !important;
528
  box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important;
529
  }
 
530
  /* 8. ๋ผ๋ฒจ ๋ฐ ํ…์ŠคํŠธ ์š”์†Œ */
531
  label,
532
  .gr-label,
 
535
  p, span, div {
536
  color: var(--text-color) !important;
537
  }
 
538
  /* 9. ์„น์…˜ ์ œ๋ชฉ */
539
  .section-title {
540
  font-size: 22px !important;
 
546
  display: flex;
547
  align-items: center;
548
  }
 
549
  .section-title span {
550
  color: var(--primary-color);
551
  }
 
552
  /* 10. ๋ฒ„ํŠผ ์Šคํƒ€์ผ๋ง */
553
  .custom-button {
554
  background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important;
 
564
  align-items: center !important;
565
  justify-content: center !important;
566
  }
 
567
  .custom-button:hover {
568
  transform: translateY(-2px) !important;
569
  box-shadow: 0 6px 12px rgba(251, 127, 13, 0.3) !important;
570
  }
 
571
  .custom-button.primary {
572
  background: linear-gradient(135deg, var(--accent-color), #ff9a8b) !important;
573
  }
 
574
  button:not([class*="custom"]):not([class*="primary"]):not([class*="secondary"]) {
575
  background-color: var(--card-bg) !important;
576
  color: var(--text-color) !important;
 
578
  border-radius: var(--border-radius) !important;
579
  transition: all 0.3s ease !important;
580
  }
 
581
  /* 11. ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ */
582
  .image-container {
583
  border-radius: var(--border-radius);
 
586
  transition: all 0.3s ease;
587
  background-color: var(--card-bg);
588
  }
 
589
  .image-container:hover {
590
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
591
  }
 
592
  /* 12. ํ…Œ์ด๋ธ” ์Šคํƒ€์ผ */
593
  table {
594
  background-color: var(--card-bg) !important;
595
  color: var(--text-color) !important;
596
  border-color: var(--border-color) !important;
597
  }
 
598
  table th {
599
  background-color: var(--primary-color) !important;
600
  color: white !important;
601
  border-color: var(--border-color) !important;
602
  }
 
603
  table td {
604
  background-color: var(--card-bg) !important;
605
  color: var(--text-color) !important;
606
  border-color: var(--border-color) !important;
607
  }
 
608
  table tbody tr:nth-child(even) {
609
  background-color: var(--table-even-bg) !important;
610
  }
 
611
  table tbody tr:hover {
612
  background-color: var(--table-hover-bg) !important;
613
  }
 
614
  /* 13. ์ฒดํฌ๋ฐ•์Šค ๋ฐ ๋ผ๋””์˜ค ๋ฒ„ํŠผ */
615
  input[type="checkbox"],
616
  input[type="radio"] {
617
  accent-color: var(--primary-color) !important;
618
  }
 
619
  /* 14. ๋ฒ„ํŠผ ๊ทธ๋ฃน */
620
  .button-grid {
621
  display: grid;
 
623
  gap: 0.8rem;
624
  margin-bottom: 1.2rem;
625
  }
 
626
  /* 15. ์Šคํฌ๋กค๋ฐ” ์Šคํƒ€์ผ */
627
  ::-webkit-scrollbar-thumb:hover {
628
  background: var(--secondary-color);
629
  }
 
630
  /* 16. ์•„์ฝ”๋””์–ธ ๋ฐ ๋“œ๋กญ๋‹ค์šด */
631
  details {
632
  background-color: var(--card-bg) !important;
633
  border-color: var(--border-color) !important;
634
  color: var(--text-color) !important;
635
  }
 
636
  details summary {
637
  background-color: var(--card-bg) !important;
638
  color: var(--text-color) !important;
639
  }
 
640
  /* 17. ํˆดํŒ ๋ฐ ํŒ์—… */
641
  [data-tooltip]:hover::after,
642
  .tooltip,
 
646
  border-color: var(--border-color) !important;
647
  box-shadow: var(--shadow-light) !important;
648
  }
 
649
  /* 18. ๋ชจ๋‹ฌ ๋ฐ ์˜ค๋ฒ„๋ ˆ์ด */
650
  .modal,
651
  .overlay,
 
655
  color: var(--text-color) !important;
656
  border-color: var(--border-color) !important;
657
  }
 
658
  /* 19. ์ถ”๊ฐ€ Gradio ์ปดํฌ๋„ŒํŠธ๋“ค */
659
  .gr-block,
660
  .gr-group,
 
663
  background-color: var(--background-color) !important;
664
  color: var(--text-color) !important;
665
  }
 
666
  /* 20. ์ฝ”๋“œ ๋ธ”๋ก ๋ฐ pre ํƒœ๊ทธ */
667
  code,
668
  pre,
 
671
  color: var(--text-color) !important;
672
  border-color: var(--border-color) !important;
673
  }
 
674
  /* 21. ์•Œ๋ฆผ ๋ฐ ๋ฉ”์‹œ์ง€ */
675
  .alert,
676
  .message,
 
682
  color: var(--text-color) !important;
683
  border-color: var(--border-color) !important;
684
  }
 
685
  /* 22. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šคํƒ€์ผ */
686
  @keyframes fadeIn {
687
  from { opacity: 0; transform: translateY(10px); }
688
  to { opacity: 1; transform: translateY(0); }
689
  }
 
690
  .fade-in {
691
  animation: fadeIn 0.5s ease-out;
692
  }
 
693
  /* 23. Examples ์„น์…˜ ์Šคํƒ€์ผ */
694
  .examples-section {
695
  display: grid;
 
697
  gap: 1.5rem;
698
  margin-top: 1rem;
699
  }
 
700
  .example-item {
701
  background-color: var(--card-bg);
702
  border-radius: var(--border-radius);
 
704
  box-shadow: var(--shadow);
705
  transition: transform 0.3s ease, background-color 0.3s ease;
706
  }
 
707
  .example-item:hover {
708
  transform: translateY(-5px);
709
  }
 
710
  /* 24. ์ „ํ™˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ */
711
  * {
712
  transition: background-color 0.3s ease,
713
  color 0.3s ease,
714
  border-color 0.3s ease !important;
715
  }
 
716
  /* 25. ๋ฐ˜์‘ํ˜• */
717
  @media (max-width: 768px) {
718
  .button-grid {
 
740
  font=[gr.themes.GoogleFont("Noto Sans KR"), "ui-sans-serif", "system-ui"]
741
  )) as demo:
742
  gr.HTML(fontawesome_link)
743
+ # ์ œ๋ชฉ ์ œ๊ฑฐ
744
+ # gr.HTML(header_html)
745
 
746
+ # ํƒญ ์ œ๊ฑฐํ•˜๊ณ  ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ ๋‚ด์šฉ์„ ์ง์ ‘ ๋ฐฐ์น˜
747
  with gr.Row(equal_height=True):
748
  with gr.Column(scale=1):
749
  # ======== ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋ฐ ์„ค์ • ์„น์…˜ ========
 
832
  )
833
 
834
  # ========== ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ ==========
835
+ # ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
836
+ image_change_btn1.click(
837
+ fn=lambda: "(#1์˜ ์—ฌ์„ฑ)์ด ์‚ด์ง ๋’ค๋กœ ๋Œ์•„๋ณด๋Š” ๋ชจ์Šต์œผ๋กœ ์ตœ๋Œ€ํ•œ ์ด์ „ seed๋ฅผ ์œ ์ง€ํ•œํ…Œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ผ.",
838
+ inputs=[],
839
+ outputs=prompt_input
840
+ )
841
+ image_change_btn2.click(
842
+ fn=lambda: "(#1 ๋ ˆ๋ชจ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋งŒ ๊ฒ€์€์ƒ‰ ๊ณ ๋ž˜๋ ˆ๊ณ ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ seed๋ฅผ ๋ณ€๊ฒฝ์„ ํ•˜์ง€๋งˆ๋ผ.",
843
+ inputs=[],
844
+ outputs=prompt_input
845
+ )
846
+ image_change_btn3.click(
847
+ fn=lambda: "(#1 ์—ฌํ–‰์šฉ ์–ผ์Œ๋ฐ•์Šค)์•ž์— ์–ผ์Œ์ด ๋‹ด๊ธด 3์ž”์˜ ์ฝœ๋ผ๊ฐ€ ๋†“์—ฌ์žˆ๋Š” ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ.",
848
+ inputs=[],
849
+ outputs=prompt_input
850
+ )
851
+
852
+ text_remove_btn.click(
853
+ fn=lambda: "(#1 ์ด๋ฏธ์ง€)์— ์žˆ๋Š” ์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜๋ผ.",
854
+ inputs=[],
855
+ outputs=prompt_input
856
+ )
857
+ text_change_btn.click(
858
+ fn=lambda: '(#1์˜ ํ…์ŠคํŠธ)๋ฅผ ์Šคํƒ€์ผ์„ ์œ ์ง€ํ•œ์ฒด ํ…์ŠคํŠธ๋งŒ "Hello"๋กœ ๋ฐ”๊ฟ”๋ผ',
859
+ inputs=[],
860
+ outputs=prompt_input
861
+ )
862
+ clothes_change_btn1.click(
863
+ fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์œ ์ง€ํ•œ ์ฒด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์™€ (#3์˜ ์ฒญ๋ฐ”์ง€)๋ฅผ ์ง์ ‘ ๋ชจ๋ธ์ด ์ฐฉ์šฉํ•œ๊ฒƒ ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ.",
864
+ inputs=[],
865
+ outputs=prompt_input
866
+ )
867
+ clothes_change_btn2.click(
868
+ fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์„ ์ฐฉ์šฉํ•˜๊ณ  (#3์˜ ๋’ท๋ฐฐ๊ฒฝ์˜ ์นดํŽ˜์ „์ฒด๊ฐ€ ๋ณด์ด๋ฉฐ) ์˜์ž์— ์•‰์•„ ์žˆ๋Š” ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ.",
869
+ inputs=[],
870
+ outputs=prompt_input
871
+ )
872
+ holding_product_btn.click(
873
+ fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด(#2์˜ ์™€์ธ์ž”)์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ.",
874
+ inputs=[],
875
+ outputs=prompt_input
876
+ )
877
+ background_change_btn.click(
878
+ fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2 ์นดํŽ˜)์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์žˆ๋Š” ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ.",
879
+ inputs=[],
880
+ outputs=prompt_input
881
+ )
882
+ composite_product_btn.click(
883
+ fn=lambda: "(#1์˜ ๋ ˆ๊ณ ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋ฅผ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์˜ ์ฃผ์š” ์š”์†Œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ ํ•ด์•ผํ•œ๋‹ค.",
884
+ inputs=[],
885
+ outputs=prompt_input
886
+ )
887
+ outpainting_btn.click(
888
+ fn=lambda: "(#1 ์ด๋ฏธ์ง€)๋ฅผ ์›๋ณธ๊ทธ๋Œ€๋กœ ์ค‘์•™์— ๋‘๊ณ  ๋น„์œจ๋กœ ์œ ์ง€ํ•œ ์ฒด ์œ„์•„๋ž˜ ๋ฐ ์ขŒ์šฐ๋กœ ํฌ๊ฒŒ ํ™•์žฅํ•˜๋ผ.",
889
+ inputs=[],
890
+ outputs=prompt_input
891
+
892
+ )
893
+ food_btn_1.click(
894
+ fn=lambda: "(#1์…€๋Ÿฌ๋“œ)์— ๋‹ด์€ ์šฉ๊ธฐ๋Š” ๋ฒ„๋ฆฌ๊ณ  ๋„“๊ณ  ํฐ ์˜ˆ์œ ์ ‘์‹œ์— (#1์…€๋Ÿฌ๋“œ)์Œ์‹๋งŒ ๊ฐ€๋“ ์ฑ„์›Œ์„œ ์ƒ์—…์ ์ธ ๊ฐ๋„๋กœ ์–ด์šธ๋ฆฌ๋Š” ์†Œํ’ˆ๊ณผ ํ•จ๊ป˜ ํ”Œ๋ ˆ์ดํŒ… ํ•œ ๋ชจ์Šต์„ ์ด๋ฏธ์ง€๋กœ ์ƒ์„ฑํ•˜๋ผ. ",
895
+ inputs=[],
896
+ outputs=prompt_input
897
+ )
898
+ food_btn_2.click(
899
+ fn=lambda: "(#2 ํ”Œ๋ ˆ์ดํŒ…ํ•œ ์ด๋ฏธ์ง€)์— ๋‹ด๊ธด ์Œ์‹์„ (#1 ์ƒ๋Ÿฌ๋“œ)๋กœ ๋ฐ”๊พธ๊ณ  ๋‚˜๋จธ์ง€๋Š” ์‹œ๋“œ๋ฅผ ์œ ์ง€ํ•œ ์ฒด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ.",
900
+ inputs=[],
901
+ outputs=prompt_input
902
+ )
903
+
904
+ food_btn_3.click(
905
+ fn=lambda: "(#1์ปต)์— ๋”ธ๊ธฐ, ๋ฐ”๋‹๋ผ, ์ดˆ์ฝ” ์•„์ด์Šคํฌ๋ฆผ์„ ๋‹ด๊ณ  ๊ทธ ์œ„์— ์ดˆ์ฝ” ์‹œ๋Ÿฝ์ด ํ๋ฅด๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ.",
906
+ inputs=[],
907
+ outputs=prompt_input
908
+ )
909
+
910
+ # ๋‹จ์ผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - API ํ‚ค ์ž…๋ ฅ๊ฐ’ ์ œ๊ฑฐ
911
+ def generate_single_image(image1, image2, image3, prompt):
912
+ return process_images_with_prompt(image1, image2, image3, prompt, 0, 3)
913
+
914
  submit_single_btn.click(
915
  fn=generate_single_image,
916
  inputs=[image1_input, image2_input, image3_input, prompt_input],
917
  outputs=[output_image1, output_text, prompt_display],
918
  )
919
 
920
+ # 2์žฅ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - API ํ‚ค ์ž…๋ ฅ๊ฐ’ ์ œ๊ฑฐ
921
  submit_btn.click(
922
  fn=generate_multiple_images,
923
  inputs=[image1_input, image2_input, image3_input, prompt_input],
924
  outputs=[output_image1, output_image2, output_text, prompt_display],
925
  )
926
 
927
+
928
+ # API ํ‚ค ์ดˆ๊ธฐํ™” ๋ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
929
+ initialize_api_keys() # API ํ‚ค ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ํ˜ธ์ถœ
930
  demo.queue()
931
+ demo.launch(share=False, inbrowser=True, width="100%") # width ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€