CanerDedeoglu commited on
Commit
bed7a91
·
verified ·
1 Parent(s): cec111a

Update handler.py

Browse files
Files changed (1) hide show
  1. handler.py +99 -11
handler.py CHANGED
@@ -8,6 +8,9 @@ from PIL import Image
8
  import requests
9
  import math
10
  import ast
 
 
 
11
 
12
  # ===== Kullanılacak HF model id =====
13
  MODEL_ID = os.getenv("HF_MODEL_ID", "PULSE-ECG/PULSE-7B")
@@ -260,22 +263,107 @@ class EndpointHandler:
260
  self.use_im_start_end = getattr(self.model.config, "mm_use_im_start_end", False)
261
 
262
  # ---- yardımcılar ----
263
- def _load_image(self, img_field: str) -> Optional[Image.Image]:
264
- """URL / base64 / path -> PIL.Image"""
265
- if not img_field:
 
 
 
 
 
266
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  try:
268
- if img_field.startswith("data:image"):
269
- _, b64 = img_field.split(",", 1)
270
- return Image.open(io.BytesIO(base64.b64decode(b64))).convert("RGB")
271
- if img_field.startswith(("http://", "https://")):
272
- r = requests.get(img_field, timeout=20)
273
- r.raise_for_status()
274
- return Image.open(io.BytesIO(r.content)).convert("RGB")
275
- return Image.open(img_field).convert("RGB")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  except Exception as e:
277
  print(f"[warn] image load failed: {e}")
278
  return None
 
 
 
279
 
280
  def _build_prompt(self, user_text: str, conv_mode: str) -> str:
281
  """Model worker tarzında prompt oluştur"""
 
8
  import requests
9
  import math
10
  import ast
11
+ from io import BytesIO
12
+ from urllib.parse import urlparse
13
+
14
 
15
  # ===== Kullanılacak HF model id =====
16
  MODEL_ID = os.getenv("HF_MODEL_ID", "PULSE-ECG/PULSE-7B")
 
263
  self.use_im_start_end = getattr(self.model.config, "mm_use_im_start_end", False)
264
 
265
  # ---- yardımcılar ----
266
+ def _load_image(self, image_input: str) -> Optional[Image.Image]:
267
+ """
268
+ URL / base64 / yerel path -> PIL.Image
269
+ - URL için: içerik tipi/uzantı kontrolü, UA header, redirect, boyut sınırı
270
+ - base64 için: data URL prefix temizliği + padding düzeltme
271
+ - path için: doğrudan aç
272
+ """
273
+ if not image_input:
274
  return None
275
+
276
+ # 25MB üstünü reddet (isteğe göre ayarlanabilir)
277
+ MAX_IMAGE_BYTES = int(os.getenv("MAX_IMAGE_BYTES", "26214400"))
278
+ ALLOWED_EXTS = {".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp"}
279
+
280
+ def _is_valid_image_format(url: str) -> bool:
281
+ try:
282
+ ext = os.path.splitext(urlparse(url).path.lower())[1]
283
+ # Uzantı yoksa da kabul et; Content-Type ile kontrol ederiz
284
+ return (ext in ALLOWED_EXTS) or (ext == "")
285
+ except Exception:
286
+ return True
287
+
288
  try:
289
+ # ---- URL input ----
290
+ if isinstance(image_input, str) and image_input.startswith(("http://", "https://")):
291
+ if not _is_valid_image_format(image_input):
292
+ print("[warn] Invalid image extension in URL")
293
+ return None
294
+
295
+ headers = {"User-Agent": "Mozilla/5.0"}
296
+ resp = requests.get(image_input, timeout=20, headers=headers, allow_redirects=True, stream=True)
297
+ resp.raise_for_status()
298
+
299
+ # Content-Type kontrolü (varsa)
300
+ ctype = resp.headers.get("Content-Type", "").lower()
301
+ if ctype and not ctype.startswith("image/"):
302
+ print(f"[warn] Non-image content-type: {ctype}")
303
+ return None
304
+
305
+ # Boyut kontrolü (Content-Length varsa)
306
+ clen = resp.headers.get("Content-Length")
307
+ if clen is not None:
308
+ try:
309
+ if int(clen) > MAX_IMAGE_BYTES:
310
+ print(f"[warn] Image too large: {clen} bytes")
311
+ return None
312
+ except Exception:
313
+ pass
314
+
315
+ # Stream’den kontrollü oku
316
+ data = resp.content
317
+ if len(data) > MAX_IMAGE_BYTES:
318
+ print(f"[warn] Image too large (actual): {len(data)} bytes")
319
+ return None
320
+
321
+ img = Image.open(BytesIO(data)).convert("RGB")
322
+ print(f"[info] URL image loaded: size={img.size}")
323
+ return img
324
+
325
+ # ---- Base64 input (data URL dahil) ----
326
+ if isinstance(image_input, str):
327
+ b64 = image_input.strip()
328
+
329
+ # data URL prefix varsa ayıkla
330
+ if b64.startswith("data:image"):
331
+ # ör: data:image/png;base64,AAAA...
332
+ if "base64," in b64:
333
+ b64 = b64.split("base64,", 1)[1]
334
+ else:
335
+ # bazen ;base64 sonrası newline vb olabilir
336
+ b64 = b64.split(",", 1)[-1]
337
+
338
+ # boşluk/newline temizliği + padding düzeltme
339
+ b64 = b64.replace("\n", "").replace("\r", "").replace(" ", "")
340
+ missing = (4 - len(b64) % 4) % 4
341
+ b64 += "=" * missing
342
+
343
+ try:
344
+ data = base64.b64decode(b64, validate=False)
345
+ if len(data) > MAX_IMAGE_BYTES:
346
+ print(f"[warn] Base64 image too large: {len(data)} bytes")
347
+ return None
348
+ img = Image.open(BytesIO(data)).convert("RGB")
349
+ print(f"[info] Base64 image loaded: size={img.size}")
350
+ return img
351
+ except Exception as e:
352
+ print(f"[warn] Base64 decode/open failed: {e}")
353
+ # Devam edip path olarak deneyeceğiz
354
+
355
+ # ---- Yerel path ----
356
+ if isinstance(image_input, str) and os.path.exists(image_input):
357
+ img = Image.open(image_input).convert("RGB")
358
+ print(f"[info] Local image loaded: size={img.size}")
359
+ return img
360
+
361
  except Exception as e:
362
  print(f"[warn] image load failed: {e}")
363
  return None
364
+
365
+ return None
366
+
367
 
368
  def _build_prompt(self, user_text: str, conv_mode: str) -> str:
369
  """Model worker tarzında prompt oluştur"""