Update app.py
Browse files
app.py
CHANGED
@@ -12,41 +12,45 @@ import logging
|
|
12 |
from google import genai
|
13 |
from google.genai import types
|
14 |
|
15 |
-
#
|
|
|
|
|
|
|
|
|
16 |
logging.basicConfig(level=logging.DEBUG,
|
17 |
format='%(asctime)s - %(levelname)s - %(message)s')
|
18 |
logger = logging.getLogger(__name__)
|
19 |
|
20 |
|
21 |
def save_binary_file(file_name, data):
|
22 |
-
logger.debug(f"
|
23 |
with open(file_name, "wb") as f:
|
24 |
f.write(data)
|
25 |
-
logger.debug(f"
|
26 |
|
27 |
|
28 |
-
def generate(text, file_name,
|
29 |
-
logger.debug(f"
|
30 |
|
31 |
try:
|
32 |
-
#
|
33 |
-
effective_api_key =
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
logger.error("
|
38 |
-
raise ValueError("API
|
39 |
|
40 |
client = genai.Client(api_key=effective_api_key)
|
41 |
-
logger.debug("Gemini
|
42 |
-
|
43 |
|
|
|
44 |
files = [
|
45 |
client.files.upload(file=file_name),
|
46 |
]
|
47 |
-
logger.debug(f"
|
48 |
-
|
49 |
|
|
|
50 |
contents = [
|
51 |
types.Content(
|
52 |
role="user",
|
@@ -59,7 +63,7 @@ def generate(text, file_name, api_key, model="gemini-2.0-flash-exp-image-generat
|
|
59 |
],
|
60 |
),
|
61 |
]
|
62 |
-
logger.debug(f"
|
63 |
|
64 |
generate_content_config = types.GenerateContentConfig(
|
65 |
temperature=1,
|
@@ -72,11 +76,11 @@ def generate(text, file_name, api_key, model="gemini-2.0-flash-exp-image-generat
|
|
72 |
],
|
73 |
response_mime_type="text/plain",
|
74 |
)
|
75 |
-
logger.debug(f"
|
76 |
|
77 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
78 |
temp_path = tmp.name
|
79 |
-
logger.debug(f"
|
80 |
|
81 |
response_stream = client.models.generate_content_stream(
|
82 |
model=model,
|
@@ -84,117 +88,106 @@ def generate(text, file_name, api_key, model="gemini-2.0-flash-exp-image-generat
|
|
84 |
config=generate_content_config,
|
85 |
)
|
86 |
|
87 |
-
logger.debug("
|
88 |
for chunk in response_stream:
|
89 |
if not chunk.candidates or not chunk.candidates[0].content or not chunk.candidates[0].content.parts:
|
90 |
-
logger.warning("
|
91 |
continue
|
92 |
|
93 |
inline_data = chunk.candidates[0].content.parts[0].inline_data
|
94 |
if inline_data:
|
95 |
save_binary_file(temp_path, inline_data.data)
|
96 |
-
logger.info(f"
|
97 |
else:
|
98 |
-
logger.info(f"
|
99 |
-
print(chunk.text)
|
100 |
|
101 |
-
# Log the raw chunk for deeper inspection
|
102 |
logger.debug(f"Raw chunk: {chunk}")
|
103 |
|
104 |
del files
|
105 |
-
logger.debug("
|
106 |
return temp_path
|
107 |
|
108 |
except Exception as e:
|
109 |
-
logger.exception("
|
110 |
-
return None
|
111 |
|
112 |
|
113 |
-
def process_image_and_prompt(composite_pil, prompt
|
114 |
-
logger.debug(f"
|
115 |
try:
|
116 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
117 |
composite_path = tmp.name
|
118 |
composite_pil.save(composite_path)
|
119 |
-
logger.debug(f"
|
120 |
|
121 |
file_name = composite_path
|
122 |
input_text = prompt
|
123 |
-
model = "gemini-2.0-flash-exp-image-generation"
|
124 |
|
125 |
-
gemma_edited_image_path = generate(text=input_text, file_name=file_name,
|
126 |
|
127 |
-
if gemma_edited_image_path:
|
128 |
-
logger.debug(f"
|
129 |
result_img = Image.open(gemma_edited_image_path)
|
130 |
if result_img.mode == "RGBA":
|
131 |
result_img = result_img.convert("RGB")
|
132 |
return [result_img]
|
133 |
else:
|
134 |
-
logger.error("generate
|
135 |
-
return [] #
|
136 |
|
137 |
except Exception as e:
|
138 |
-
logger.exception("
|
139 |
-
return [] #
|
140 |
|
141 |
|
142 |
-
# --- Gradio
|
143 |
with gr.Blocks() as demo:
|
144 |
gr.HTML(
|
145 |
"""
|
146 |
<div style='display: flex; align-items: center; justify-content: center; gap: 20px'>
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
</div>
|
155 |
</div>
|
156 |
"""
|
157 |
)
|
158 |
-
gr.Markdown("
|
159 |
|
160 |
with gr.Row():
|
161 |
with gr.Column():
|
162 |
-
image_input = gr.Image(type="pil", label="
|
163 |
-
gemini_api_key = gr.Textbox(
|
164 |
-
lines=1,
|
165 |
-
placeholder="Gemini API Key แแแทแบแแซ",
|
166 |
-
label="Gemini API Key",
|
167 |
-
type="password"
|
168 |
-
)
|
169 |
prompt_input = gr.Textbox(
|
170 |
lines=2,
|
171 |
-
placeholder="
|
172 |
-
label="
|
173 |
)
|
174 |
-
submit_btn = gr.Button("
|
175 |
with gr.Column():
|
176 |
-
output_gallery = gr.Gallery(label="
|
177 |
|
178 |
submit_btn.click(
|
179 |
fn=process_image_and_prompt,
|
180 |
-
inputs=[image_input, prompt_input
|
181 |
outputs=output_gallery,
|
182 |
)
|
183 |
-
|
184 |
-
# ---
|
185 |
-
#
|
186 |
dummy_image = Image.new("RGBA", (100, 100), color="red")
|
187 |
-
dummy_prompt = "
|
188 |
-
dummy_api_key = os.environ.get("GEMINI_API_KEY") # Or put a placeholder key here for testing
|
189 |
|
190 |
-
|
191 |
-
|
192 |
-
result = process_image_and_prompt(dummy_image, dummy_prompt, dummy_api_key)
|
193 |
|
194 |
if result:
|
195 |
-
logger.info(f"
|
196 |
-
# result[0].show() # Uncomment to display image if running locally
|
197 |
else:
|
198 |
-
logger.error("
|
199 |
|
200 |
-
demo.launch(share=True)
|
|
|
12 |
from google import genai
|
13 |
from google.genai import types
|
14 |
|
15 |
+
# .env ํ์ผ์ ์ ์ฅ๋ ํ๊ฒฝ๋ณ์ ๋ก๋ (python-dotenv ์ค์น ํ์: pip install python-dotenv)
|
16 |
+
from dotenv import load_dotenv
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
# ๋ก๊น
์ค์ (๋ก๊ทธ ๋ ๋ฒจ: DEBUG)
|
20 |
logging.basicConfig(level=logging.DEBUG,
|
21 |
format='%(asctime)s - %(levelname)s - %(message)s')
|
22 |
logger = logging.getLogger(__name__)
|
23 |
|
24 |
|
25 |
def save_binary_file(file_name, data):
|
26 |
+
logger.debug(f"ํ์ผ์ ์ด์ง ๋ฐ์ดํฐ ์ ์ฅ ์ค: {file_name}")
|
27 |
with open(file_name, "wb") as f:
|
28 |
f.write(data)
|
29 |
+
logger.debug(f"ํ์ผ ์ ์ฅ ์๋ฃ: {file_name}")
|
30 |
|
31 |
|
32 |
+
def generate(text, file_name, model="gemini-2.0-flash-exp-image-generation"):
|
33 |
+
logger.debug(f"generate ํจ์ ์์ - ํ
์คํธ: '{text}', ํ์ผ๋ช
: '{file_name}', ๋ชจ๋ธ: '{model}'")
|
34 |
|
35 |
try:
|
36 |
+
# API ํค๋ ํ๊ฒฝ๋ณ์์์ ๋ถ๋ฌ์ด
|
37 |
+
effective_api_key = os.environ.get("GEMINI_API_KEY")
|
38 |
+
if effective_api_key:
|
39 |
+
logger.debug("ํ๊ฒฝ๋ณ์์์ API ํค ๋ถ๋ฌ์ด")
|
40 |
+
else:
|
41 |
+
logger.error("API ํค๊ฐ ํ๊ฒฝ๋ณ์์ ์ค์ ๋์ง ์์์ต๋๋ค.")
|
42 |
+
raise ValueError("API ํค๊ฐ ํ์ํฉ๋๋ค.")
|
43 |
|
44 |
client = genai.Client(api_key=effective_api_key)
|
45 |
+
logger.debug("Gemini ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์๋ฃ.")
|
|
|
46 |
|
47 |
+
# ํ์ผ ์
๋ก๋
|
48 |
files = [
|
49 |
client.files.upload(file=file_name),
|
50 |
]
|
51 |
+
logger.debug(f"ํ์ผ ์
๋ก๋ ์๋ฃ. URI: {files[0].uri}, MIME ํ์
: {files[0].mime_type}")
|
|
|
52 |
|
53 |
+
# ์ปจํ
์ธ ๊ฐ์ฒด ์์ฑ: ํ์ผ URI์ ํ
์คํธ ํ๋กฌํํธ๋ฅผ ํจ๊ป ํฌํจ
|
54 |
contents = [
|
55 |
types.Content(
|
56 |
role="user",
|
|
|
63 |
],
|
64 |
),
|
65 |
]
|
66 |
+
logger.debug(f"์ปจํ
์ธ ๊ฐ์ฒด ์์ฑ ์๋ฃ: {contents}")
|
67 |
|
68 |
generate_content_config = types.GenerateContentConfig(
|
69 |
temperature=1,
|
|
|
76 |
],
|
77 |
response_mime_type="text/plain",
|
78 |
)
|
79 |
+
logger.debug(f"์์ฑ ์ค์ : {generate_content_config}")
|
80 |
|
81 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
82 |
temp_path = tmp.name
|
83 |
+
logger.debug(f"์์ ํ์ผ ์์ฑ๋จ: {temp_path}")
|
84 |
|
85 |
response_stream = client.models.generate_content_stream(
|
86 |
model=model,
|
|
|
88 |
config=generate_content_config,
|
89 |
)
|
90 |
|
91 |
+
logger.debug("์๋ต ์คํธ๋ฆผ ์ฒ๋ฆฌ ์์...")
|
92 |
for chunk in response_stream:
|
93 |
if not chunk.candidates or not chunk.candidates[0].content or not chunk.candidates[0].content.parts:
|
94 |
+
logger.warning("chunk์ ํ๋ณด, ์ปจํ
์ธ , ๋๋ ํํธ๊ฐ ์์ต๋๋ค. ๊ฑด๋๋๋๋ค.")
|
95 |
continue
|
96 |
|
97 |
inline_data = chunk.candidates[0].content.parts[0].inline_data
|
98 |
if inline_data:
|
99 |
save_binary_file(temp_path, inline_data.data)
|
100 |
+
logger.info(f"MIME ํ์
{inline_data.mime_type}์ ํ์ผ์ด ์ ์ฅ๋จ: {temp_path} (ํ๋กฌํํธ: {text})")
|
101 |
else:
|
102 |
+
logger.info(f"์์ ๋ ํ
์คํธ: {chunk.text}")
|
103 |
+
print(chunk.text)
|
104 |
|
|
|
105 |
logger.debug(f"Raw chunk: {chunk}")
|
106 |
|
107 |
del files
|
108 |
+
logger.debug("์
๋ก๋๋ ํ์ผ ์ ๋ณด ์ญ์ ์๋ฃ.")
|
109 |
return temp_path
|
110 |
|
111 |
except Exception as e:
|
112 |
+
logger.exception("์ด๋ฏธ์ง ์์ฑ ์ค ์ค๋ฅ ๋ฐ์:")
|
113 |
+
return None # ์ค๋ฅ ๋ฐ์ ์ None ๋ฐํ
|
114 |
|
115 |
|
116 |
+
def process_image_and_prompt(composite_pil, prompt):
|
117 |
+
logger.debug(f"process_image_and_prompt ํจ์ ์์ - ํ๋กฌํํธ: '{prompt}'")
|
118 |
try:
|
119 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
120 |
composite_path = tmp.name
|
121 |
composite_pil.save(composite_path)
|
122 |
+
logger.debug(f"ํฉ์ฑ ์ด๋ฏธ์ง ์ ์ฅ ์๋ฃ: {composite_path}")
|
123 |
|
124 |
file_name = composite_path
|
125 |
input_text = prompt
|
126 |
+
model = "gemini-2.0-flash-exp-image-generation"
|
127 |
|
128 |
+
gemma_edited_image_path = generate(text=input_text, file_name=file_name, model=model)
|
129 |
|
130 |
+
if gemma_edited_image_path:
|
131 |
+
logger.debug(f"์ด๋ฏธ์ง ์์ฑ ์๋ฃ. ๊ฒฝ๋ก: {gemma_edited_image_path}")
|
132 |
result_img = Image.open(gemma_edited_image_path)
|
133 |
if result_img.mode == "RGBA":
|
134 |
result_img = result_img.convert("RGB")
|
135 |
return [result_img]
|
136 |
else:
|
137 |
+
logger.error("generate ํจ์์์ None ๋ฐํ๋จ.")
|
138 |
+
return [] # ์ค๋ฅ ์ ๋น ๋ฆฌ์คํธ ๋ฐํ
|
139 |
|
140 |
except Exception as e:
|
141 |
+
logger.exception("process_image_and_prompt ํจ์์์ ์ค๋ฅ ๋ฐ์:")
|
142 |
+
return [] # ์ค๋ฅ ์ ๋น ๋ฆฌ์คํธ ๋ฐํ
|
143 |
|
144 |
|
145 |
+
# --- Gradio ์ธํฐํ์ด์ค ๊ตฌ์ฑ ---
|
146 |
with gr.Blocks() as demo:
|
147 |
gr.HTML(
|
148 |
"""
|
149 |
<div style='display: flex; align-items: center; justify-content: center; gap: 20px'>
|
150 |
+
<div style="background-color: var(--block-background-fill); border-radius: 8px">
|
151 |
+
<img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" style="width: 100px; height: 100px;">
|
152 |
+
</div>
|
153 |
+
<div>
|
154 |
+
<h1>Gemini๋ฅผ ์ด์ฉํ ์ด๋ฏธ์ง ํธ์ง</h1>
|
155 |
+
<p>Gemini API ํค๋ ํ๊ฒฝ๋ณ์(GEMINI_API_KEY)๋ก ์ค์ ๋์ด ์์ต๋๋ค.</p>
|
156 |
+
</div>
|
|
|
157 |
</div>
|
158 |
"""
|
159 |
)
|
160 |
+
gr.Markdown("์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ๊ณ , ํธ์งํ ๋ด์ฉ์ ์
๋ ฅํ์ธ์.")
|
161 |
|
162 |
with gr.Row():
|
163 |
with gr.Column():
|
164 |
+
image_input = gr.Image(type="pil", label="์ด๋ฏธ์ง ์
๋ก๋", image_mode="RGBA")
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
prompt_input = gr.Textbox(
|
166 |
lines=2,
|
167 |
+
placeholder="ํธ์งํ ๋ด์ฉ์ ์
๋ ฅํ์ธ์...",
|
168 |
+
label="ํธ์ง ํ๋กฌํํธ"
|
169 |
)
|
170 |
+
submit_btn = gr.Button("์ด๋ฏธ์ง ํธ์ง ์คํ")
|
171 |
with gr.Column():
|
172 |
+
output_gallery = gr.Gallery(label="ํธ์ง ๊ฒฐ๊ณผ")
|
173 |
|
174 |
submit_btn.click(
|
175 |
fn=process_image_and_prompt,
|
176 |
+
inputs=[image_input, prompt_input],
|
177 |
outputs=output_gallery,
|
178 |
)
|
179 |
+
|
180 |
+
# --- ํ
์คํธ ์ฝ๋ ---
|
181 |
+
# ํ
์คํธ์ฉ ๋๋ฏธ ์ด๋ฏธ์ง (์ค์ ์ด๋ฏธ์ง๋ก ๋์ฒด ๊ฐ๋ฅ)
|
182 |
dummy_image = Image.new("RGBA", (100, 100), color="red")
|
183 |
+
dummy_prompt = "์ด๋ฏธ์ง๋ฅผ ํ๋์์ผ๋ก ๋ณ๊ฒฝํด์ค"
|
|
|
184 |
|
185 |
+
logger.info("process_image_and_prompt ํจ์๋ฅผ ์ง์ ํธ์ถํฉ๋๋ค...")
|
186 |
+
result = process_image_and_prompt(dummy_image, dummy_prompt)
|
|
|
187 |
|
188 |
if result:
|
189 |
+
logger.info(f"์ง์ ํธ์ถ ์ฑ๊ณต. ๊ฒฐ๊ณผ: {result}")
|
|
|
190 |
else:
|
191 |
+
logger.error("์ง์ ํธ์ถ ์คํจ.")
|
192 |
|
193 |
+
demo.launch(share=True)
|