hasanbasbunar commited on
Commit
cc7516e
·
verified ·
1 Parent(s): d1cb872

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -36
app.py CHANGED
@@ -5,16 +5,12 @@ from PIL import Image
5
  import os
6
  import io
7
  import uuid
 
 
8
  from dotenv import load_dotenv
9
  load_dotenv()
10
 
11
  # --- CONFIGURATION ---
12
- api_key = os.environ.get("GOOGLE_API_KEY")
13
-
14
- client = None
15
- if api_key:
16
- client = genai.Client(api_key=api_key)
17
-
18
  MODELS = {
19
  "🧠 Gemini 3 Pro Preview (Recommended)": "gemini-3-pro-image-preview",
20
  "⚡ Gemini 2.5 Flash (Fast)": "gemini-2.5-flash-image"
@@ -28,11 +24,9 @@ TEMP_CHAT_DIR = "temp_chat_images"
28
  os.makedirs(TEMP_CHAT_DIR, exist_ok=True)
29
 
30
  # --- UTILS ---
31
-
32
- def get_client():
33
- if not client:
34
- raise gr.Error("API Key missing. Please set GOOGLE_API_KEY environment variable.")
35
- return client
36
 
37
  def safe_process_image(part):
38
  """Converts raw data to PIL Image."""
@@ -81,20 +75,15 @@ def process_response(response):
81
 
82
  def update_api_key(new_key):
83
  """Updates the global client with the user's API key."""
84
- global client
85
  if not new_key:
86
- return "⚠️ Please enter a valid API Key."
87
-
88
- try:
89
- # Attempt to initialize the client
90
- client = genai.Client(api_key=new_key)
91
- return "✅ API Key configured successfully! You can now use the application."
92
- except Exception as e:
93
- return f"❌ Configuration Error: {str(e)}"
94
 
95
- def generate_studio(prompt, model_ui, ratio, resolution, grounding):
 
 
96
  """Standard T2I Generation"""
97
- cli = get_client()
98
  model_name = MODELS[model_ui]
99
 
100
  img_conf = {"aspect_ratio": ratio}
@@ -118,9 +107,9 @@ def generate_studio(prompt, model_ui, ratio, resolution, grounding):
118
  except Exception as e:
119
  raise gr.Error(f"API Error: {str(e)}")
120
 
121
- def generate_composition(prompt, files, model_ui, ratio, resolution):
122
  """Composition I2I"""
123
- cli = get_client()
124
  model_name = MODELS[model_ui]
125
 
126
  if not files: raise gr.Error("No input images provided.")
@@ -157,8 +146,8 @@ def generate_composition(prompt, files, model_ui, ratio, resolution):
157
 
158
  # --- CHAT LOGIC ---
159
 
160
- def init_chat_session(model_ui, grounding):
161
- cli = get_client()
162
  model_name = MODELS[model_ui]
163
 
164
  tools = None
@@ -174,10 +163,10 @@ def init_chat_session(model_ui, grounding):
174
  )
175
  return chat
176
 
177
- def chat_respond(message, history, chat_state, img_input, model_ui, grounding):
178
  """Iterative chat management with image history"""
179
  if chat_state is None:
180
- chat_state = init_chat_session(model_ui, grounding)
181
 
182
  # --- 1. User message prep ---
183
  contents = [message]
@@ -238,8 +227,11 @@ def chat_respond(message, history, chat_state, img_input, model_ui, grounding):
238
  bot_err_obj = {"role": "assistant", "content": err_msg}
239
  return "", history + [user_message_obj, bot_err_obj], chat_state, []
240
 
241
- def clear_chat(model_ui, grounding):
242
- new_chat = init_chat_session(model_ui, grounding)
 
 
 
243
  return [], new_chat, []
244
 
245
  # --- GRADIO INTERFACE ---
@@ -256,6 +248,8 @@ with gr.Blocks(title="Nano Vision Studio") as demo:
256
  gr.Markdown("# Nano 🍌 Vision Studio")
257
  gr.Markdown("### The Ultimate Interface: 4K Generation, Grounding, Multi-Image Composition & Iterative Chat")
258
 
 
 
259
  # Chat Session State
260
  chat_state = gr.State(None)
261
 
@@ -287,7 +281,7 @@ with gr.Blocks(title="Nano Vision Studio") as demo:
287
  api_btn.click(
288
  update_api_key,
289
  inputs=[api_input],
290
- outputs=[api_status]
291
  )
292
 
293
  # --- TAB 1 : CREATION STUDIO ---
@@ -319,7 +313,7 @@ with gr.Blocks(title="Nano Vision Studio") as demo:
319
 
320
  t1_btn.click(
321
  generate_studio,
322
- inputs=[t1_prompt, t1_model, t1_ratio, t1_res, t1_grounding],
323
  outputs=[t1_gallery, t1_text, t1_thought_imgs, t1_thought_txt]
324
  )
325
 
@@ -344,7 +338,7 @@ with gr.Blocks(title="Nano Vision Studio") as demo:
344
 
345
  t2_btn.click(
346
  generate_composition,
347
- inputs=[t2_prompt, t2_files, t2_model, t2_ratio, t2_res],
348
  outputs=[t2_gallery, t2_text]
349
  )
350
 
@@ -375,13 +369,13 @@ with gr.Blocks(title="Nano Vision Studio") as demo:
375
 
376
  chat_btn.click(
377
  chat_respond,
378
- inputs=[chat_input, chat_history, chat_state, chat_img, c_model, c_grounding],
379
  outputs=[chat_input, chat_history, chat_state, chat_gallery_zoom]
380
  )
381
 
382
  clear_btn.click(
383
  clear_chat,
384
- inputs=[c_model, c_grounding],
385
  outputs=[chat_history, chat_state, chat_gallery_zoom]
386
  )
387
 
@@ -411,7 +405,34 @@ with gr.Blocks(title="Nano Vision Studio") as demo:
411
  - **Resolution**: Use the "Pro" model to unlock 4K output.
412
  """)
413
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  if __name__ == "__main__":
 
 
415
  # Configuration Gradio 6.0
416
  demo.queue(default_concurrency_limit=20)
417
 
@@ -420,5 +441,8 @@ if __name__ == "__main__":
420
  theme=gr.themes.Soft(),
421
  css=css,
422
  max_threads=40,
423
- show_error=True
 
 
 
424
  )
 
5
  import os
6
  import io
7
  import uuid
8
+ import threading
9
+ import time
10
  from dotenv import load_dotenv
11
  load_dotenv()
12
 
13
  # --- CONFIGURATION ---
 
 
 
 
 
 
14
  MODELS = {
15
  "🧠 Gemini 3 Pro Preview (Recommended)": "gemini-3-pro-image-preview",
16
  "⚡ Gemini 2.5 Flash (Fast)": "gemini-2.5-flash-image"
 
24
  os.makedirs(TEMP_CHAT_DIR, exist_ok=True)
25
 
26
  # --- UTILS ---
27
+ def get_client(api_key):
28
+ if not api_key: raise gr.Error("API Key manquante")
29
+ return genai.Client(api_key=api_key)
 
 
30
 
31
  def safe_process_image(part):
32
  """Converts raw data to PIL Image."""
 
75
 
76
  def update_api_key(new_key):
77
  """Updates the global client with the user's API key."""
78
+ # global client # NOT global for HF Spaces
79
  if not new_key:
80
+ return "⚠️ Clé invalide", None # None ne changera pas l'état
 
 
 
 
 
 
 
81
 
82
+ return "✅ Clé enregistrée pour cette session !", new_key
83
+
84
+ def generate_studio(prompt, model_ui, ratio, resolution, grounding, user_api_key):
85
  """Standard T2I Generation"""
86
+ cli = get_client(user_api_key)
87
  model_name = MODELS[model_ui]
88
 
89
  img_conf = {"aspect_ratio": ratio}
 
107
  except Exception as e:
108
  raise gr.Error(f"API Error: {str(e)}")
109
 
110
+ def generate_composition(prompt, files, model_ui, ratio, resolution, user_api_key):
111
  """Composition I2I"""
112
+ cli = get_client(user_api_key)
113
  model_name = MODELS[model_ui]
114
 
115
  if not files: raise gr.Error("No input images provided.")
 
146
 
147
  # --- CHAT LOGIC ---
148
 
149
+ def init_chat_session(model_ui, grounding, user_api_key):
150
+ cli = get_client(user_api_key)
151
  model_name = MODELS[model_ui]
152
 
153
  tools = None
 
163
  )
164
  return chat
165
 
166
+ def chat_respond(message, history, chat_state, img_input, model_ui, grounding, user_api_key):
167
  """Iterative chat management with image history"""
168
  if chat_state is None:
169
+ chat_state = init_chat_session(model_ui, grounding, user_api_key)
170
 
171
  # --- 1. User message prep ---
172
  contents = [message]
 
227
  bot_err_obj = {"role": "assistant", "content": err_msg}
228
  return "", history + [user_message_obj, bot_err_obj], chat_state, []
229
 
230
+ def clear_chat(model_ui, grounding, user_api_key):
231
+ # Si pas de clé, on retourne juste vide, sinon on recrée un chat
232
+ if not user_api_key:
233
+ return [], None, []
234
+ new_chat = init_chat_session(model_ui, grounding, user_api_key)
235
  return [], new_chat, []
236
 
237
  # --- GRADIO INTERFACE ---
 
248
  gr.Markdown("# Nano 🍌 Vision Studio")
249
  gr.Markdown("### The Ultimate Interface: 4K Generation, Grounding, Multi-Image Composition & Iterative Chat")
250
 
251
+ # État pour stocker la clé API de CET utilisateur spécifique
252
+ user_api_key_state = gr.State(os.environ.get("GOOGLE_API_KEY", ""))
253
  # Chat Session State
254
  chat_state = gr.State(None)
255
 
 
281
  api_btn.click(
282
  update_api_key,
283
  inputs=[api_input],
284
+ outputs=[api_status, user_api_key_state]
285
  )
286
 
287
  # --- TAB 1 : CREATION STUDIO ---
 
313
 
314
  t1_btn.click(
315
  generate_studio,
316
+ inputs=[t1_prompt, t1_model, t1_ratio, t1_res, t1_grounding, user_api_key_state],
317
  outputs=[t1_gallery, t1_text, t1_thought_imgs, t1_thought_txt]
318
  )
319
 
 
338
 
339
  t2_btn.click(
340
  generate_composition,
341
+ inputs=[t2_prompt, t2_files, t2_model, t2_ratio, t2_res, user_api_key_state],
342
  outputs=[t2_gallery, t2_text]
343
  )
344
 
 
369
 
370
  chat_btn.click(
371
  chat_respond,
372
+ inputs=[chat_input, chat_history, chat_state, chat_img, c_model, c_grounding, user_api_key_state],
373
  outputs=[chat_input, chat_history, chat_state, chat_gallery_zoom]
374
  )
375
 
376
  clear_btn.click(
377
  clear_chat,
378
+ inputs=[c_model, c_grounding, user_api_key_state],
379
  outputs=[chat_history, chat_state, chat_gallery_zoom]
380
  )
381
 
 
405
  - **Resolution**: Use the "Pro" model to unlock 4K output.
406
  """)
407
 
408
+ def cleanup_old_files():
409
+ """Supprime les fichiers vieux de plus de 1h toutes les 10 minutes."""
410
+ while True:
411
+ try:
412
+ now = time.time()
413
+ # 3600 secondes = 1 heure
414
+ cutoff = now - 3600
415
+
416
+ if os.path.exists(TEMP_CHAT_DIR):
417
+ for filename in os.listdir(TEMP_CHAT_DIR):
418
+ filepath = os.path.join(TEMP_CHAT_DIR, filename)
419
+ if os.path.isfile(filepath):
420
+ # Si le fichier est plus vieux que le cutoff
421
+ if os.path.getmtime(filepath) < cutoff:
422
+ try:
423
+ os.remove(filepath)
424
+ print(f"🧹 Supprimé : {filename}")
425
+ except Exception:
426
+ pass # On ignore les erreurs ponctuelles
427
+ except Exception as e:
428
+ print(f"⚠️ Erreur worker : {e}")
429
+
430
+ # Pause de 30 minutes entre chaque vérification
431
+ time.sleep(1800)
432
+
433
  if __name__ == "__main__":
434
+ # Démarrage du nettoyage en arrière-plan (daemon=True pour qu'il s'arrête quand l'app quitte)
435
+ threading.Thread(target=cleanup_old_files, daemon=True).start()
436
  # Configuration Gradio 6.0
437
  demo.queue(default_concurrency_limit=20)
438
 
 
441
  theme=gr.themes.Soft(),
442
  css=css,
443
  max_threads=40,
444
+ show_error=True,
445
+ server_name="0.0.0.0",
446
+ server_port=7860,
447
+ share=False
448
  )