Omarelrayes commited on
Commit
43dfaa2
·
verified ·
1 Parent(s): f336f6c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +93 -294
app.py CHANGED
@@ -8,25 +8,18 @@ import sys
8
  from pathlib import Path
9
  from PIL import Image
10
  import gradio as gr
11
- from fastapi import FastAPI
12
- from fastapi.middleware.cors import CORSMiddleware
13
- from pydantic import BaseModel
14
- from contextlib import asynccontextmanager
15
 
16
- # إعداد Logging شامل
17
  logging.basicConfig(
18
  level=logging.INFO,
19
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
20
- handlers=[
21
- logging.StreamHandler(sys.stdout)
22
- ]
23
  )
24
  logger = logging.getLogger(__name__)
25
 
26
  logger.info("🚀 بدء تشغيل DermaScan AI...")
27
 
28
  try:
29
- logger.info("📦 جاري استيراد الوحدات...")
30
  from rag import build_default_vectorstore
31
  from agent import (
32
  build_agent,
@@ -42,7 +35,6 @@ except Exception as e:
42
 
43
  # Configuration
44
  MODEL_NAME = "llama-3.1-8b-instant"
45
- FALLBACK_MODEL_NAME = "llama-3.3-70b-versatile"
46
  TEMPERATURE = 0.25
47
  TOP_K = 3
48
  EXTERNAL_API_URL = "https://omarelrayes-api.hf.space"
@@ -50,7 +42,6 @@ EXTERNAL_API_URL = "https://omarelrayes-api.hf.space"
50
  # Global variables
51
  SESSIONS = {}
52
  agent = None
53
- fallback_agent = None
54
  api_client = None
55
 
56
  def get_session(thread_id: str) -> dict:
@@ -59,225 +50,64 @@ def get_session(thread_id: str) -> dict:
59
  "role": "patient",
60
  "last_analysis": None,
61
  "current_image": None,
62
- "matched_patient_id": None,
63
- "matched_patient_report": None,
64
  "_image_blobs": {},
65
  "_session_image_names": [],
66
- "_turn_count": 0,
67
- "_summary": "",
68
  }
69
  return SESSIONS[thread_id]
70
 
71
  def initialize_system():
72
- """Initialize agent and API client"""
73
- global agent, fallback_agent, api_client
74
 
75
  try:
76
  logger.info("🔧 بدء تهيئة النظام...")
77
 
78
- # Check Groq API Key
79
  groq_key = os.environ.get("GROQ_API_KEY")
80
  if not groq_key:
81
- logger.warning("️ GROQ_API_KEY not set in environment")
82
  else:
83
  logger.info("✅ GROQ_API_KEY found")
84
 
85
- # Initialize API client
86
- logger.info("🌐 جاري تهيئة API Client...")
87
  api_client = DermaScanAPIClient(EXTERNAL_API_URL)
88
  set_api_client(api_client)
89
 
90
- # Check external API health
91
- try:
92
- if api_client.health_check():
93
- logger.info(" External API is healthy")
94
- else:
95
- logger.warning("⚠️ External API not reachable")
96
- except Exception as e:
97
- logger.error(f"❌ External API health check failed: {e}")
98
-
99
- # Build vectorstore and agent
100
- logger.info("🗄️ جاري بناء Vectorstore...")
101
- try:
102
- vectorstore = build_default_vectorstore()
103
- if vectorstore:
104
- logger.info("✅ Vectorstore loaded successfully")
105
- retriever = vectorstore.as_retriever(search_kwargs={"k": TOP_K})
106
- else:
107
- logger.warning("⚠️ Vectorstore not available")
108
- retriever = None
109
- except Exception as e:
110
- logger.error(f" Failed to build vectorstore: {e}", exc_info=True)
111
- retriever = None
112
 
113
- # Build agents
114
- logger.info("🤖 جاري بناء Agent...")
115
- try:
116
- agent = build_agent(retriever, model_name=MODEL_NAME, temperature=TEMPERATURE)
117
- logger.info("✅ Primary agent built successfully")
118
- except Exception as e:
119
- logger.error(f"❌ Failed to build primary agent: {e}", exc_info=True)
120
- agent = None
121
 
122
- try:
123
- fallback_agent = build_agent(retriever, model_name=FALLBACK_MODEL_NAME, temperature=TEMPERATURE)
124
- logger.info("✅ Fallback agent built successfully")
125
- except Exception as e:
126
- logger.error(f" Failed to build fallback agent: {e}", exc_info=True)
127
- fallback_agent = None
128
 
 
 
129
  logger.info("🎉 تم تهيئة النظام بنجاح!")
130
 
131
  except Exception as e:
132
- logger.error(f"❌ فشل تهيئة النظام: {e}", exc_info=True)
133
  raise
134
 
135
- # FastAPI app with lifespan
136
- @asynccontextmanager
137
- async def lifespan(app: FastAPI):
138
- # Startup
139
- logger.info("�� FastAPI startup...")
140
- initialize_system()
141
- yield
142
- # Shutdown
143
- logger.info("🛑 FastAPI shutdown...")
144
-
145
- app = FastAPI(title="DermaScan AI - RAG System", lifespan=lifespan)
146
-
147
- app.add_middleware(
148
- CORSMiddleware,
149
- allow_origins=["*"],
150
- allow_methods=["*"],
151
- allow_headers=["*"],
152
- )
153
-
154
- # Pydantic models
155
- class ChatRequest(BaseModel):
156
- thread_id: str
157
- message: str
158
 
159
- class BookingRequest(BaseModel):
160
- thread_id: str
161
- city: str = ""
162
-
163
- # FastAPI endpoints
164
- @app.get("/health")
165
- def health():
166
- return {
167
- "agent_ready": agent is not None,
168
- "api_client_ready": api_client is not None,
169
- "external_api_healthy": api_client.health_check() if api_client else False,
170
- }
171
-
172
- @app.post("/chat")
173
- def chat(req: ChatRequest):
174
- """Chat endpoint"""
175
- global agent
176
-
177
- if agent is None:
178
- return {"ok": False, "error": "Agent not initialized yet"}
179
-
180
- session = get_session(req.thread_id)
181
-
182
- # Set session context
183
- set_session_storage(SESSIONS)
184
- set_current_thread_id(req.thread_id)
185
-
186
- try:
187
- # Build prompt with context
188
- last_analysis = session.get("last_analysis")
189
- analysis_ctx = ""
190
- if last_analysis:
191
- analysis_ctx = (
192
- f"\n\n[CURRENT IMAGE ANALYSIS — Classification: {last_analysis.get('label', 'N/A')}, "
193
- f"Confidence: {last_analysis.get('confidence_pct', 0):.1f}%, "
194
- f"Affected Area: {last_analysis.get('infection_pct', 0):.1f}%]"
195
- )
196
-
197
- full_prompt = req.message + analysis_ctx
198
-
199
- # Run agent
200
- config = {"configurable": {"thread_id": req.thread_id}, "recursion_limit": 14}
201
-
202
- final_text = ""
203
- tool_calls = []
204
-
205
- for event in agent.stream(
206
- {"messages": [("user", full_prompt)]},
207
- config=config,
208
- stream_mode="values"
209
- ):
210
- last_msg = event["messages"][-1]
211
- if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
212
- for tc in last_msg.tool_calls:
213
- tool_calls.append({"name": tc["name"], "args": tc["args"]})
214
- if last_msg.type == "ai" and last_msg.content:
215
- final_text = last_msg.content
216
-
217
- # Clean response
218
- cleaned_text = re.sub(r'\[ANALYSIS_RESULT:\{.*?\}\]', '', final_text).strip()
219
-
220
- session["_turn_count"] = session.get("_turn_count", 0) + 1
221
-
222
- return {
223
- "ok": True,
224
- "reply": cleaned_text,
225
- "tool_calls": tool_calls,
226
- }
227
-
228
- except Exception as e:
229
- logger.error(f"Chat error: {e}", exc_info=True)
230
- return {"ok": False, "error": str(e)}
231
-
232
- @app.post("/book-appointment")
233
- def book_appointment(req: BookingRequest):
234
- """Book appointment via external API"""
235
- if api_client is None:
236
- return {"ok": False, "error": "API client not initialized"}
237
-
238
- result = api_client.book_appointment(req.thread_id, req.city)
239
- return result
240
-
241
- @app.get("/image")
242
- def get_image(name: str = "", thread_id: str = ""):
243
- """Get image from session"""
244
- try:
245
- session = get_session(thread_id) if thread_id else {}
246
- blobs = session.get("_image_blobs", {})
247
-
248
- if name not in blobs:
249
- return {"ok": False, "error": "Image not found"}
250
-
251
- return {"ok": True, "base64": blobs[name], "media_type": "image/png"}
252
- except Exception as e:
253
- return {"ok": False, "error": str(e)}
254
-
255
- # Gradio Interface
256
  def gradio_chat(message: str, history: list, thread_id: str = "default") -> str:
257
- """Gradio chat function"""
258
  if agent is None:
259
- return "System is not initialized yet. Please wait..."
260
 
261
  session = get_session(thread_id)
262
-
263
- # Set session context
264
  set_session_storage(SESSIONS)
265
  set_current_thread_id(thread_id)
266
 
267
  try:
268
- # Build prompt with context
269
  last_analysis = session.get("last_analysis")
270
  analysis_ctx = ""
271
  if last_analysis:
272
- analysis_ctx = (
273
- f"\n\n[CURRENT IMAGE ANALYSIS — Classification: {last_analysis.get('label', 'N/A')}, "
274
- f"Confidence: {last_analysis.get('confidence_pct', 0):.1f}%, "
275
- f"Affected Area: {last_analysis.get('infection_pct', 0):.1f}%]"
276
- )
277
 
278
  full_prompt = message + analysis_ctx
279
-
280
- # Run agent
281
  config = {"configurable": {"thread_id": thread_id}, "recursion_limit": 14}
282
 
283
  final_text = ""
@@ -290,30 +120,25 @@ def gradio_chat(message: str, history: list, thread_id: str = "default") -> str:
290
  if last_msg.type == "ai" and last_msg.content:
291
  final_text = last_msg.content
292
 
293
- # Clean response
294
  cleaned_text = re.sub(r'\[ANALYSIS_RESULT:\{.*?\}\]', '', final_text).strip()
295
-
296
  session["_turn_count"] = session.get("_turn_count", 0) + 1
297
 
298
- return cleaned_text if cleaned_text else "No response generated."
299
 
300
  except Exception as e:
301
- logger.error(f"Gradio chat error: {e}", exc_info=True)
302
  return f"Error: {str(e)}"
303
 
304
  def gradio_upload_image(image: Image.Image, thread_id: str = "default") -> tuple:
305
- """Handle image upload"""
306
  if api_client is None:
307
  return "API client not initialized", None, None, None
308
 
309
  try:
310
- # Upload and analyze
311
  result = api_client.upload_and_analyze_image(image, thread_id)
312
 
313
  if 'error' in result:
314
  return f"Error: {result['error']}", None, None, None
315
 
316
- # Update session
317
  session = get_session(thread_id)
318
  session["last_analysis"] = {
319
  "label": result.get("label"),
@@ -321,41 +146,27 @@ def gradio_upload_image(image: Image.Image, thread_id: str = "default") -> tuple
321
  "infection_pct": result.get("infection_pct"),
322
  }
323
 
324
- # Store images
325
  if 'images' in result:
326
  session["_image_blobs"].update(result['images'])
327
 
328
- # Prepare display
329
  analysis_text = f"""
330
- ✅ **تم تحليل الصورة بنجاح!**
 
331
  **النتائج:**
332
  - التصنيف: {result.get('label')}
333
  - نسبة الثقة: {result.get('confidence_pct')}%
334
  - المساحة المصابة: {result.get('infection_pct')}%
335
- يمكنك الآن طرح أسئلة عن هذه الصورة.
 
336
  """
337
 
338
- # Get images for display
339
  orig_b64 = result.get('images', {}).get('original', '')
340
  mask_b64 = result.get('images', {}).get('mask', '')
341
  overlay_b64 = result.get('images', {}).get('overlay', '')
342
 
343
- # Convert base64 to numpy arrays
344
- orig_img = None
345
- mask_img = None
346
- overlay_img = None
347
-
348
- if orig_b64:
349
- orig_data = base64.b64decode(orig_b64)
350
- orig_img = Image.open(io.BytesIO(orig_data))
351
-
352
- if mask_b64:
353
- mask_data = base64.b64decode(mask_b64)
354
- mask_img = Image.open(io.BytesIO(mask_data))
355
-
356
- if overlay_b64:
357
- overlay_data = base64.b64decode(overlay_b64)
358
- overlay_img = Image.open(io.BytesIO(overlay_data))
359
 
360
  return analysis_text, orig_img, mask_img, overlay_img
361
 
@@ -364,84 +175,72 @@ def gradio_upload_image(image: Image.Image, thread_id: str = "default") -> tuple
364
  return f"Error: {str(e)}", None, None, None
365
 
366
  # Build Gradio UI
367
- logger.info(" جاري بناء واجهة Gradio...")
368
- try:
369
- with gr.Blocks(title="DermaScan AI - Patient Portal") as demo:
370
- gr.Markdown("""
371
- # 🏥 DermaScan AI - مساعد أمراض الجلدية
372
-
373
- ارفع صورة الجلد واحصل على تحليل بالذكاء الاصطناعي + اسأل أي أسئلة.
374
- """)
375
-
376
- thread_id_state = gr.State(value="default")
377
-
378
- with gr.Row():
379
- with gr.Column(scale=1):
380
- gr.Markdown("### 📤 رفع الصورة")
381
- image_input = gr.Image(type="pil", label="ارفع صورة الجلد")
382
- upload_btn = gr.Button("📎 ارفع وحلّل", variant="primary")
383
- analysis_output = gr.Markdown(label="نتيجة التحليل")
384
-
385
- with gr.Column(scale=2):
386
- gr.Markdown("### الدردشة مع DermaScan AI")
387
- chatbot = gr.Chatbot(label="المحادثة", height=400)
388
- msg_input = gr.Textbox(
389
- label="رسالتك",
390
- placeholder="اسأل عن حالتك الجلدية...",
391
- lines=2
392
- )
393
- send_btn = gr.Button("🚀 إرسال", variant="primary")
394
-
395
- with gr.Row():
396
- with gr.Column():
397
- gr.Markdown("### 🖼️ الصور المحللة")
398
- orig_display = gr.Image(label="الصورة الأصلية", type="pil")
399
- with gr.Column():
400
- mask_display = gr.Image(label="قناع التجزئة", type="pil")
401
- with gr.Column():
402
- overlay_display = gr.Image(label="الصورة مع التغطية", type="pil")
403
-
404
- # Event handlers
405
- def user_message(user_msg, history):
406
- return "", history + [[user_msg, None]]
407
-
408
- def bot_response(history, thread_id):
409
- if not history:
410
- return history
411
-
412
- user_msg = history[-1][0]
413
- response = gradio_chat(user_msg, history[:-1], thread_id)
414
-
415
- history[-1][1] = response
416
  return history
417
-
418
- upload_btn.click(
419
- fn=gradio_upload_image,
420
- inputs=[image_input, thread_id_state],
421
- outputs=[analysis_output, orig_display, mask_display, overlay_display]
422
- )
423
-
424
- send_btn.click(
425
- fn=user_message,
426
- inputs=[msg_input, chatbot],
427
- outputs=[msg_input, chatbot]
428
- ).then(
429
- fn=bot_response,
430
- inputs=[chatbot, thread_id_state],
431
- outputs=[chatbot]
432
- )
433
 
434
- logger.info("✅ تم بناء واجهة Gradio بنجاح")
 
 
 
 
435
 
436
- except Exception as e:
437
- logger.error(f"❌ فشل بناء واجهة Gradio: {e}", exc_info=True)
438
- raise
439
-
440
- logger.info("🎉 التطبيق جاهز للتشغيل!")
 
 
 
 
441
 
442
- # الحل الصحيح لـ HF Spaces:
443
- # Mount Gradio to FastAPI
444
- app = gr.mount_gradio_app(app, demo, path="/")
445
- logger.info("✅ تم ربط Gradio بـ FastAPI")
446
 
447
- # HF Spaces بيتكفل بتشغيل التطبيق - مش محتاج demo.launch()
 
 
 
 
 
 
8
  from pathlib import Path
9
  from PIL import Image
10
  import gradio as gr
 
 
 
 
11
 
12
+ # إعداد Logging
13
  logging.basicConfig(
14
  level=logging.INFO,
15
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
16
+ handlers=[logging.StreamHandler(sys.stdout)]
 
 
17
  )
18
  logger = logging.getLogger(__name__)
19
 
20
  logger.info("🚀 بدء تشغيل DermaScan AI...")
21
 
22
  try:
 
23
  from rag import build_default_vectorstore
24
  from agent import (
25
  build_agent,
 
35
 
36
  # Configuration
37
  MODEL_NAME = "llama-3.1-8b-instant"
 
38
  TEMPERATURE = 0.25
39
  TOP_K = 3
40
  EXTERNAL_API_URL = "https://omarelrayes-api.hf.space"
 
42
  # Global variables
43
  SESSIONS = {}
44
  agent = None
 
45
  api_client = None
46
 
47
  def get_session(thread_id: str) -> dict:
 
50
  "role": "patient",
51
  "last_analysis": None,
52
  "current_image": None,
 
 
53
  "_image_blobs": {},
54
  "_session_image_names": [],
 
 
55
  }
56
  return SESSIONS[thread_id]
57
 
58
  def initialize_system():
59
+ global agent, api_client
 
60
 
61
  try:
62
  logger.info("🔧 بدء تهيئة النظام...")
63
 
 
64
  groq_key = os.environ.get("GROQ_API_KEY")
65
  if not groq_key:
66
+ logger.warning("️ GROQ_API_KEY not set")
67
  else:
68
  logger.info("✅ GROQ_API_KEY found")
69
 
 
 
70
  api_client = DermaScanAPIClient(EXTERNAL_API_URL)
71
  set_api_client(api_client)
72
 
73
+ if api_client.health_check():
74
+ logger.info("✅ External API is healthy")
75
+ else:
76
+ logger.warning("⚠️ External API not reachable")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
+ vectorstore = build_default_vectorstore()
79
+ retriever = vectorstore.as_retriever(search_kwargs={"k": TOP_K}) if vectorstore else None
 
 
 
 
 
 
80
 
81
+ if retriever is None:
82
+ logger.warning("⚠️ Vectorstore not available")
 
 
 
 
83
 
84
+ agent = build_agent(retriever, model_name=MODEL_NAME, temperature=TEMPERATURE)
85
+ logger.info("✅ Agent built successfully")
86
  logger.info("🎉 تم تهيئة النظام بنجاح!")
87
 
88
  except Exception as e:
89
+ logger.error(f"❌ فشل التهيئة: {e}", exc_info=True)
90
  raise
91
 
92
+ # Initialize on startup
93
+ initialize_system()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
+ # Gradio Functions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  def gradio_chat(message: str, history: list, thread_id: str = "default") -> str:
 
97
  if agent is None:
98
+ return "System not initialized yet."
99
 
100
  session = get_session(thread_id)
 
 
101
  set_session_storage(SESSIONS)
102
  set_current_thread_id(thread_id)
103
 
104
  try:
 
105
  last_analysis = session.get("last_analysis")
106
  analysis_ctx = ""
107
  if last_analysis:
108
+ analysis_ctx = f"\n\n[Analysis: {last_analysis.get('label', 'N/A')} - {last_analysis.get('confidence_pct', 0):.1f}%]"
 
 
 
 
109
 
110
  full_prompt = message + analysis_ctx
 
 
111
  config = {"configurable": {"thread_id": thread_id}, "recursion_limit": 14}
112
 
113
  final_text = ""
 
120
  if last_msg.type == "ai" and last_msg.content:
121
  final_text = last_msg.content
122
 
 
123
  cleaned_text = re.sub(r'\[ANALYSIS_RESULT:\{.*?\}\]', '', final_text).strip()
 
124
  session["_turn_count"] = session.get("_turn_count", 0) + 1
125
 
126
+ return cleaned_text if cleaned_text else "No response."
127
 
128
  except Exception as e:
129
+ logger.error(f"Chat error: {e}", exc_info=True)
130
  return f"Error: {str(e)}"
131
 
132
  def gradio_upload_image(image: Image.Image, thread_id: str = "default") -> tuple:
 
133
  if api_client is None:
134
  return "API client not initialized", None, None, None
135
 
136
  try:
 
137
  result = api_client.upload_and_analyze_image(image, thread_id)
138
 
139
  if 'error' in result:
140
  return f"Error: {result['error']}", None, None, None
141
 
 
142
  session = get_session(thread_id)
143
  session["last_analysis"] = {
144
  "label": result.get("label"),
 
146
  "infection_pct": result.get("infection_pct"),
147
  }
148
 
 
149
  if 'images' in result:
150
  session["_image_blobs"].update(result['images'])
151
 
 
152
  analysis_text = f"""
153
+ ✅ **تم التحليل بنجاح!**
154
+
155
  **النتائج:**
156
  - التصنيف: {result.get('label')}
157
  - نسبة الثقة: {result.get('confidence_pct')}%
158
  - المساحة المصابة: {result.get('infection_pct')}%
159
+
160
+ يمكنك الآن طرح أسئلة عن الصورة.
161
  """
162
 
 
163
  orig_b64 = result.get('images', {}).get('original', '')
164
  mask_b64 = result.get('images', {}).get('mask', '')
165
  overlay_b64 = result.get('images', {}).get('overlay', '')
166
 
167
+ orig_img = Image.open(io.BytesIO(base64.b64decode(orig_b64))) if orig_b64 else None
168
+ mask_img = Image.open(io.BytesIO(base64.b64decode(mask_b64))) if mask_b64 else None
169
+ overlay_img = Image.open(io.BytesIO(base64.b64decode(overlay_b64))) if overlay_b64 else None
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  return analysis_text, orig_img, mask_img, overlay_img
172
 
 
175
  return f"Error: {str(e)}", None, None, None
176
 
177
  # Build Gradio UI
178
+ with gr.Blocks(title="DermaScan AI - Patient Portal") as demo:
179
+ gr.Markdown("""
180
+ # DermaScan AI - مساعد أمراض الجلدية
181
+
182
+ ارفع صورة الجلد واحصل على تحليل بالذكاء الاصطناعي.
183
+ """)
184
+
185
+ thread_id_state = gr.State(value="default")
186
+
187
+ with gr.Row():
188
+ with gr.Column(scale=1):
189
+ gr.Markdown("### رفع الصورة")
190
+ image_input = gr.Image(type="pil", label="ارفع صورة الجلد")
191
+ upload_btn = gr.Button(" ارفع وحلّل", variant="primary")
192
+ analysis_output = gr.Markdown(label="نتيجة التحليل")
193
+
194
+ with gr.Column(scale=2):
195
+ gr.Markdown("### 💬 الدردشة")
196
+ chatbot = gr.Chatbot(label="المحادثة", height=400)
197
+ msg_input = gr.Textbox(
198
+ label="رسالتك",
199
+ placeholder="اسأل عن حالتك...",
200
+ lines=2
201
+ )
202
+ send_btn = gr.Button("🚀 إرسال", variant="primary")
203
+
204
+ with gr.Row():
205
+ with gr.Column():
206
+ orig_display = gr.Image(label="الأصلية", type="pil")
207
+ with gr.Column():
208
+ mask_display = gr.Image(label="القناع", type="pil")
209
+ with gr.Column():
210
+ overlay_display = gr.Image(label="التغطية", type="pil")
211
+
212
+ def user_message(user_msg, history):
213
+ return "", history + [[user_msg, None]]
214
+
215
+ def bot_response(history, thread_id):
216
+ if not history:
 
 
 
 
 
 
 
 
 
 
217
  return history
218
+ user_msg = history[-1][0]
219
+ response = gradio_chat(user_msg, history[:-1], thread_id)
220
+ history[-1][1] = response
221
+ return history
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
+ upload_btn.click(
224
+ fn=gradio_upload_image,
225
+ inputs=[image_input, thread_id_state],
226
+ outputs=[analysis_output, orig_display, mask_display, overlay_display]
227
+ )
228
 
229
+ send_btn.click(
230
+ fn=user_message,
231
+ inputs=[msg_input, chatbot],
232
+ outputs=[msg_input, chatbot]
233
+ ).then(
234
+ fn=bot_response,
235
+ inputs=[chatbot, thread_id_state],
236
+ outputs=[chatbot]
237
+ )
238
 
239
+ logger.info("🎉 التطبيق جاهز!")
 
 
 
240
 
241
+ # تشغيل Gradio مباشرة - ده الحل الصحيح لـ HF Spaces
242
+ demo.launch(
243
+ server_name="0.0.0.0",
244
+ server_port=7860,
245
+ debug=False
246
+ )