Update app.py
Browse files
app.py
CHANGED
@@ -12,45 +12,41 @@ import logging
|
|
12 |
from google import genai
|
13 |
from google.genai import types
|
14 |
|
15 |
-
#
|
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"
|
27 |
with open(file_name, "wb") as f:
|
28 |
f.write(data)
|
29 |
-
logger.debug(f"
|
30 |
|
31 |
|
32 |
-
def generate(text, file_name, model="gemini-2.0-flash-exp-image-generation"):
|
33 |
-
logger.debug(f"generate
|
34 |
|
35 |
try:
|
36 |
-
#
|
37 |
-
effective_api_key = os.environ.get("GEMINI_API_KEY")
|
38 |
-
if
|
39 |
-
|
40 |
-
|
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"
|
|
|
52 |
|
53 |
-
# ์ปจํ
์ธ ๊ฐ์ฒด ์์ฑ: ํ์ผ URI์ ํ
์คํธ ํ๋กฌํํธ๋ฅผ ํจ๊ป ํฌํจ
|
54 |
contents = [
|
55 |
types.Content(
|
56 |
role="user",
|
@@ -63,7 +59,7 @@ def generate(text, file_name, model="gemini-2.0-flash-exp-image-generation"):
|
|
63 |
],
|
64 |
),
|
65 |
]
|
66 |
-
logger.debug(f"
|
67 |
|
68 |
generate_content_config = types.GenerateContentConfig(
|
69 |
temperature=1,
|
@@ -76,11 +72,11 @@ def generate(text, file_name, model="gemini-2.0-flash-exp-image-generation"):
|
|
76 |
],
|
77 |
response_mime_type="text/plain",
|
78 |
)
|
79 |
-
logger.debug(f"
|
80 |
|
81 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
82 |
temp_path = tmp.name
|
83 |
-
logger.debug(f"
|
84 |
|
85 |
response_stream = client.models.generate_content_stream(
|
86 |
model=model,
|
@@ -88,106 +84,117 @@ def generate(text, file_name, model="gemini-2.0-flash-exp-image-generation"):
|
|
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("
|
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"
|
101 |
else:
|
102 |
-
logger.info(f"
|
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
|
114 |
|
115 |
|
116 |
-
def process_image_and_prompt(composite_pil, prompt):
|
117 |
-
logger.debug(f"process_image_and_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"
|
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"
|
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
|
138 |
-
return [] #
|
139 |
|
140 |
except Exception as e:
|
141 |
-
logger.exception("
|
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 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
|
|
157 |
</div>
|
158 |
"""
|
159 |
)
|
160 |
-
gr.Markdown("
|
161 |
|
162 |
with gr.Row():
|
163 |
with gr.Column():
|
164 |
-
image_input = gr.Image(type="pil", label="
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
186 |
-
|
|
|
187 |
|
188 |
if result:
|
189 |
-
logger.info(f"
|
|
|
190 |
else:
|
191 |
-
logger.error("
|
192 |
|
193 |
-
demo.launch(share=True)
|
|
|
12 |
from google import genai
|
13 |
from google.genai import types
|
14 |
|
15 |
+
# Configure logging
|
|
|
|
|
|
|
|
|
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"Saving binary data to file: {file_name}")
|
23 |
with open(file_name, "wb") as f:
|
24 |
f.write(data)
|
25 |
+
logger.debug(f"File saved successfully: {file_name}")
|
26 |
|
27 |
|
28 |
+
def generate(text, file_name, api_key, model="gemini-2.0-flash-exp-image-generation"):
|
29 |
+
logger.debug(f"Starting generate function with text: '{text}', file_name: '{file_name}', model: '{model}'")
|
30 |
|
31 |
try:
|
32 |
+
# Initialize client
|
33 |
+
effective_api_key = api_key.strip() if api_key and api_key.strip() != "" else os.environ.get("GEMINI_API_KEY")
|
34 |
+
logger.debug(f"Using API Key: {'Provided' if api_key.strip() else 'From Environment Variable'}")
|
35 |
+
|
36 |
+
if not effective_api_key:
|
37 |
+
logger.error("No API key provided or found in environment variable.")
|
38 |
+
raise ValueError("API key is required.")
|
39 |
|
40 |
client = genai.Client(api_key=effective_api_key)
|
41 |
+
logger.debug("Gemini client initialized.")
|
42 |
+
|
43 |
|
|
|
44 |
files = [
|
45 |
client.files.upload(file=file_name),
|
46 |
]
|
47 |
+
logger.debug(f"File uploaded. URI: {files[0].uri}, MIME Type: {files[0].mime_type}")
|
48 |
+
|
49 |
|
|
|
50 |
contents = [
|
51 |
types.Content(
|
52 |
role="user",
|
|
|
59 |
],
|
60 |
),
|
61 |
]
|
62 |
+
logger.debug(f"Content object created: {contents}")
|
63 |
|
64 |
generate_content_config = types.GenerateContentConfig(
|
65 |
temperature=1,
|
|
|
72 |
],
|
73 |
response_mime_type="text/plain",
|
74 |
)
|
75 |
+
logger.debug(f"Generate content config: {generate_content_config}")
|
76 |
|
77 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
78 |
temp_path = tmp.name
|
79 |
+
logger.debug(f"Temporary file created: {temp_path}")
|
80 |
|
81 |
response_stream = client.models.generate_content_stream(
|
82 |
model=model,
|
|
|
84 |
config=generate_content_config,
|
85 |
)
|
86 |
|
87 |
+
logger.debug("Starting to process response stream...")
|
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("Chunk has no candidates, content, or parts. Skipping.")
|
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"File of mime type {inline_data.mime_type} saved to: {temp_path} and prompt input :{text}")
|
97 |
else:
|
98 |
+
logger.info(f"Received text: {chunk.text}")
|
99 |
+
print(chunk.text) # Keep the print for immediate console output
|
100 |
|
101 |
+
# Log the raw chunk for deeper inspection
|
102 |
logger.debug(f"Raw chunk: {chunk}")
|
103 |
|
104 |
del files
|
105 |
+
logger.debug("Uploaded files deleted.")
|
106 |
return temp_path
|
107 |
|
108 |
except Exception as e:
|
109 |
+
logger.exception("An error occurred during generation:") # This will log the full traceback
|
110 |
+
return None # Return None when error happens
|
111 |
|
112 |
|
113 |
+
def process_image_and_prompt(composite_pil, prompt, gemini_api_key):
|
114 |
+
logger.debug(f"Starting process_image_and_prompt with prompt: '{prompt}'")
|
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"Composite image saved to: {composite_path}")
|
120 |
|
121 |
file_name = composite_path
|
122 |
input_text = prompt
|
123 |
+
model = "gemini-2.0-flash-exp-image-generation" # Consider changing this to "gemini-pro-vision"
|
124 |
|
125 |
+
gemma_edited_image_path = generate(text=input_text, file_name=file_name, api_key=gemini_api_key, model=model)
|
126 |
|
127 |
+
if gemma_edited_image_path: # Check none or not
|
128 |
+
logger.debug(f"Image generated at path: {gemma_edited_image_path}")
|
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 function returned None.")
|
135 |
+
return [] # Return empty when error
|
136 |
|
137 |
except Exception as e:
|
138 |
+
logger.exception("Error occurred in process_image_and_prompt")
|
139 |
+
return [] # Return empty when error
|
140 |
|
141 |
|
142 |
+
# --- Gradio Interface ---
|
143 |
with gr.Blocks() as demo:
|
144 |
gr.HTML(
|
145 |
"""
|
146 |
<div style='display: flex; align-items: center; justify-content: center; gap: 20px'>
|
147 |
+
<div style="background-color: var(--block-background-fill); border-radius: 8px">
|
148 |
+
<img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" style="width: 100px; height: 100px;">
|
149 |
+
</div>
|
150 |
+
<div>
|
151 |
+
<h1></h1>
|
152 |
+
<p>แแฏแถแแญแแบแแแบแธแแผแแบแแแบ Gemini</p>
|
153 |
+
<p>API Key แแญแฏ <a href="https://aistudio.google.com/apikey">แคแแฑแแฌ</a> แแฝแแบ แแแบแแฎแธแแซ</p>
|
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="แแฏแถแแแบแแแบ", image_mode="RGBA")
|
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, gemini_api_key],
|
181 |
outputs=output_gallery,
|
182 |
)
|
183 |
+
|
184 |
+
# --- Test Code ---
|
185 |
+
# Create a dummy image (replace with your actual image if needed)
|
186 |
dummy_image = Image.new("RGBA", (100, 100), color="red")
|
187 |
+
dummy_prompt = "Make the image blue"
|
188 |
+
dummy_api_key = os.environ.get("GEMINI_API_KEY") # Or put a placeholder key here for testing
|
189 |
|
190 |
+
# Call the function directly
|
191 |
+
logger.info("Calling process_image_and_prompt directly...")
|
192 |
+
result = process_image_and_prompt(dummy_image, dummy_prompt, dummy_api_key)
|
193 |
|
194 |
if result:
|
195 |
+
logger.info(f"Direct call successful. Result: {result}")
|
196 |
+
# result[0].show() # Uncomment to display image if running locally
|
197 |
else:
|
198 |
+
logger.error("Direct call failed.")
|
199 |
|
200 |
+
demo.launch(share=True) # gradio launch last
|