CanerDedeoglu commited on
Commit
c334a27
·
verified ·
1 Parent(s): 6183d4f

Update handler.py

Browse files
Files changed (1) hide show
  1. handler.py +140 -70
handler.py CHANGED
@@ -1,12 +1,10 @@
1
  # -*- coding: utf-8 -*-
2
- # handler.py — Rapid_ECG / PULSE-7B için HF Inference Endpoints custom handler
3
- # - LLAVA otomatik kurulum (requirements'a yazmak zorunda değilsin)
4
- # - EndpointHandler.load()/__call__(inputs) sözleşmesi
5
- # - {"inputs": {...}} ve düz payload formatlarını destekler
6
- # - URL / base64 / yerel yol görüntü girişi
7
- # - <image> sentineli (+ IM_START/END gerekiyorsa)
8
- # - attention_mask fix + echo-fix
9
- # - CUDA: bf16/fp16, CPU: fp32
10
  import os
11
  import io
12
  import sys
@@ -22,18 +20,19 @@ import requests
22
  def _ensure_llava(tag: str = "v1.2.0"):
23
  try:
24
  import llava # noqa
 
25
  return
26
  except ImportError:
27
- print(f"⚡ LLaVA bulunamadı; kuruluyor (tag={tag})...")
28
  subprocess.check_call([
29
  sys.executable, "-m", "pip", "install",
30
  f"git+https://github.com/haotian-liu/LLaVA@{tag}#egg=llava"
31
  ])
32
- print("✅ LLaVA kuruldu.")
33
 
34
  _ensure_llava("v1.2.0")
35
 
36
- # Artık llava import edebiliriz
37
  from llava.conversation import conv_templates
38
  from llava.constants import (
39
  DEFAULT_IMAGE_TOKEN,
@@ -46,9 +45,11 @@ from llava.mm_utils import process_images, tokenizer_image_token
46
  # (gerekirse fallback için)
47
  try:
48
  from transformers import AutoProcessor, CLIPImageProcessor # type: ignore
49
- except Exception:
 
50
  AutoProcessor = None
51
  CLIPImageProcessor = None
 
52
 
53
  # ---------- yardımcılar ----------
54
  def _get_env(name: str, default: Optional[str] = None) -> Optional[str]:
@@ -56,16 +57,21 @@ def _get_env(name: str, default: Optional[str] = None) -> Optional[str]:
56
  return v if v not in (None, "") else default
57
 
58
  def _pick_device() -> torch.device:
 
59
  if torch.cuda.is_available():
60
- return torch.device("cuda")
61
- if torch.backends.mps.is_available():
62
- return torch.device("mps")
63
- return torch.device("cpu")
 
64
 
65
  def _pick_dtype(device: torch.device):
66
  if device.type == "cuda":
67
- return torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
68
- return torch.float32
 
 
 
69
 
70
  def _is_probably_base64(s: str) -> bool:
71
  s = s.strip()
@@ -75,33 +81,44 @@ def _is_probably_base64(s: str) -> bool:
75
  return len(s) % 4 == 0 and all(c in allowed for c in s)
76
 
77
  def _load_image_from_any(image_input: Any) -> Image.Image:
78
- """PIL.Image | path | url | data-url | raw base64 | bytes | file-like"""
79
  if isinstance(image_input, Image.Image):
 
80
  return image_input.convert("RGB")
81
  if isinstance(image_input, (bytes, bytearray)):
 
82
  return Image.open(io.BytesIO(image_input)).convert("RGB")
83
  if hasattr(image_input, "read"):
 
84
  return Image.open(image_input).convert("RGB")
85
  if isinstance(image_input, str):
86
  s = image_input.strip()
87
  # data URL
88
  if s.startswith("data:image"):
89
- _, b64 = s.split(",", 1)
90
- data = base64.b64decode(b64)
91
- return Image.open(io.BytesIO(data)).convert("RGB")
92
- # raw base64 (best-effort)
 
 
 
 
 
93
  if _is_probably_base64(s) and not s.startswith(("http://", "https://")):
 
94
  try:
95
  data = base64.b64decode(s)
96
  return Image.open(io.BytesIO(data)).convert("RGB")
97
- except Exception:
98
- pass
99
  # URL
100
  if s.startswith(("http://", "https://")):
 
101
  resp = requests.get(s, timeout=20)
102
  resp.raise_for_status()
103
  return Image.open(io.BytesIO(resp.content)).convert("RGB")
104
  # path
 
105
  return Image.open(s).convert("RGB")
106
  raise ValueError(f"Unsupported image input type: {type(image_input)}")
107
 
@@ -127,7 +144,6 @@ def _build_prompt_with_image(prompt: str, model_cfg) -> str:
127
  return f"{token}\n{prompt}"
128
  return f"{DEFAULT_IMAGE_TOKEN}\n{prompt}"
129
 
130
- # ---- image_processor yoksa oluşturmak için yardımcılar ----
131
  def _maybe_get_vision_tower_from_cfg(cfg) -> Optional[str]:
132
  vt = getattr(cfg, "vision_tower", None)
133
  if isinstance(vt, (list, tuple)) and vt:
@@ -142,61 +158,61 @@ class _ProcessorWrapper:
142
  self.proc = proc
143
  def preprocess(self, image, return_tensors="pt"):
144
  out = self.proc(image, return_tensors=return_tensors)
145
- # AutoProcessor bazen dict döner, bazen tensor; normalize edelim
146
  if isinstance(out, dict):
147
  return out
148
  return {"pixel_values": out}
149
 
150
  def _ensure_image_processor(image_processor, model_cfg, model_path: str):
 
151
  if image_processor is not None:
152
- # bazı AutoProcessor'larda gerçek işleyici proc.image_processor altında
153
  if hasattr(image_processor, "preprocess"):
 
154
  return image_processor
155
  if hasattr(image_processor, "image_processor"):
156
  ip = image_processor.image_processor
 
157
  if hasattr(ip, "preprocess"):
158
  return ip
159
  return _ProcessorWrapper(ip)
160
  if callable(image_processor):
 
161
  return _ProcessorWrapper(image_processor)
162
 
163
- # 1) AutoProcessor (trust_remote_code ile) dene
164
  if AutoProcessor is not None:
165
  try:
 
166
  proc = AutoProcessor.from_pretrained(model_path, trust_remote_code=True)
167
  if hasattr(proc, "preprocess"):
 
168
  return proc
169
  if hasattr(proc, "image_processor"):
170
  ip = proc.image_processor
 
171
  if hasattr(ip, "preprocess"):
172
  return ip
173
  return _ProcessorWrapper(ip)
 
174
  return _ProcessorWrapper(proc)
175
- except Exception:
176
- pass
177
 
178
- # 2) Vision tower'dan CLIPImageProcessor üret
179
  vt = _maybe_get_vision_tower_from_cfg(model_cfg)
 
180
  if vt and CLIPImageProcessor is not None:
181
  try:
182
  ip = CLIPImageProcessor.from_pretrained(vt)
 
183
  return ip
184
- except Exception:
185
- pass
186
 
187
- # 3) en sonda None kalsın; çağrı tarafında fallback var
188
  return None
189
 
190
  # ---------- Endpoint Handler ----------
191
  class EndpointHandler:
192
- """
193
- HF Inference Toolkit çağrı akışı:
194
- handler = EndpointHandler(model_dir)
195
- handler.load()
196
- handler(inputs_dict)
197
- """
198
-
199
  def __init__(self, model_dir: Optional[str] = None):
 
200
  self.model_dir = model_dir
201
  self.model = None
202
  self.tokenizer = None
@@ -209,10 +225,13 @@ class EndpointHandler:
209
  def load(self):
210
  model_path = _get_env("HF_MODEL_ID", "PULSE-ECG/PULSE-7B")
211
  model_base = _get_env("HF_MODEL_BASE", None)
 
212
 
213
  os.environ.setdefault("ATTN_IMPLEMENTATION", "flash_attention_2")
214
  os.environ.setdefault("FLASH_ATTENTION", "1")
 
215
 
 
216
  self.tokenizer, self.model, self.image_processor, self.context_len = load_pretrained_model(
217
  model_path=model_path,
218
  model_base=model_base,
@@ -222,86 +241,130 @@ class EndpointHandler:
222
  device=self.device,
223
  )
224
  self.model_name = getattr(self.model.config, "name_or_path", str(model_path))
 
 
225
 
226
- # image_processor fallback (kritik!)
227
  self.image_processor = _ensure_image_processor(self.image_processor, self.model.config, model_path)
 
228
 
229
  # tokenizer güvenliği
230
  try:
231
  self.tokenizer.padding_side = "left"
232
  if self.tokenizer.pad_token_id is None:
233
  self.tokenizer.pad_token_id = self.tokenizer.eos_token_id
234
- except Exception:
235
- pass
 
236
 
237
  self.model.eval()
 
238
  return True
239
 
240
  @torch.inference_mode()
241
  def __call__(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
242
- # HF bazen payload'ı {"inputs": {...}} diye sarar
 
243
  if "inputs" in inputs and isinstance(inputs["inputs"], dict):
 
244
  inputs = inputs["inputs"]
245
 
246
- # ---- girişler
247
  prompt = inputs.get("query") or inputs.get("prompt") or inputs.get("istem") or ""
248
  image_in = inputs.get("image") or inputs.get("image_url") or inputs.get("img")
 
 
249
  if not image_in:
250
  return {"error": "Missing 'image' in payload"}
251
  if not isinstance(prompt, str) or not prompt.strip():
252
  return {"error": "Missing 'query'/'prompt' text"}
253
 
254
- temperature = float(inputs.get("temperature", 0.0)) # 0 => deterministic
255
- top_p = float(inputs.get("top_p", 0.9))
256
- max_new = int(inputs.get("max_new_tokens", inputs.get("max_tokens", 512)))
257
- repetition_penalty = float(inputs.get("repetition_penalty", 1.0))
258
- conv_mode_override = inputs.get("conv_mode") or _get_env("CONV_MODE", None)
 
 
 
 
259
 
260
  # ---- görüntü
261
  try:
262
  image = _load_image_from_any(image_in)
 
263
  except Exception as e:
 
264
  return {"error": f"Failed to load image: {e}"}
 
265
  images = [image]
266
  image_sizes = [image.size]
 
267
 
268
- # process_images -> tensör (image_processor None olabilir; o zaman plain preprocess)
269
  try:
270
  if self.image_processor is None:
271
- # en kaba yedek: AutoProcessor başarısız olduysa
272
- raise RuntimeError("image_processor is None")
273
  images_tensor = process_images(images, self.image_processor, self.model.config)
274
- except Exception:
275
- # plain preprocess
276
- if hasattr(self.image_processor, "preprocess"):
277
- images_tensor = self.image_processor.preprocess(image, return_tensors="pt")["pixel_values"]
278
- else:
279
- # en son çare: AutoProcessor benzeri çağrı
280
- images_tensor = _ProcessorWrapper(self.image_processor).preprocess(image, return_tensors="pt")["pixel_values"]
281
- images_tensor = images_tensor.to(self.device, dtype=self.dtype)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
283
  # ---- konuşma şablonu + prompt
284
  mode = conv_mode_override or _get_conv_mode(self.model_name)
285
  conv = (conv_templates.get(mode) or conv_templates[list(conv_templates.keys())[0]]).copy()
 
286
 
287
  formatted_prompt = _build_prompt_with_image(prompt.strip(), self.model.config)
 
288
  conv.append_message(conv.roles[0], formatted_prompt)
289
  conv.append_message(conv.roles[1], None)
290
  full_prompt = conv.get_prompt()
 
291
 
292
- # ---- tokenizasyon (image token aware) + attention_mask
293
  try:
294
  input_ids = tokenizer_image_token(
295
  full_prompt, self.tokenizer, image_token_index=-200, return_tensors="pt"
296
  ).unsqueeze(0).to(self.device)
297
- except Exception:
298
- toks = self.tokenizer([full_prompt], return_tensors="pt", padding=True, truncation=True)
299
- input_ids = toks["input_ids"].to(self.device)
 
 
 
 
 
 
 
300
 
301
  attention_mask = torch.ones_like(input_ids, device=self.device)
 
302
 
303
  # ---- generate
304
  try:
 
305
  gen_ids = self.model.generate(
306
  input_ids=input_ids,
307
  attention_mask=attention_mask,
@@ -314,12 +377,19 @@ class EndpointHandler:
314
  repetition_penalty=repetition_penalty,
315
  use_cache=True,
316
  )
 
317
  except Exception as e:
 
318
  return {"error": f"Generation failed: {e}"}
319
 
320
- # ---- sadece yeni tokenları decode et (echo yok)
321
- new_tokens = gen_ids[0, input_ids.shape[1]:]
322
- text = self.tokenizer.decode(new_tokens, skip_special_tokens=True).strip()
 
 
 
 
 
323
 
324
  return {
325
  "generated_text": text,
 
1
  # -*- coding: utf-8 -*-
2
+ # handler.py — Rapid_ECG / PULSE-7B — DEBUG'li sürüm
3
+ # - LLAVA otomatik kurulum (tag sabit)
4
+ # - HF Endpoint sözleşmesi (EndpointHandler(model_dir).load().__call__(inputs))
5
+ # - {"inputs": {...}} sarmalamasını da destekler
6
+ # - Her kritik adımda kapsamlı print(DEBUG) ekli
7
+
 
 
8
  import os
9
  import io
10
  import sys
 
20
  def _ensure_llava(tag: str = "v1.2.0"):
21
  try:
22
  import llava # noqa
23
+ print(f"[DEBUG] LLaVA already available.")
24
  return
25
  except ImportError:
26
+ print(f"[DEBUG] LLaVA not found; installing (tag={tag}) ...")
27
  subprocess.check_call([
28
  sys.executable, "-m", "pip", "install",
29
  f"git+https://github.com/haotian-liu/LLaVA@{tag}#egg=llava"
30
  ])
31
+ print("[DEBUG] LLaVA installed.")
32
 
33
  _ensure_llava("v1.2.0")
34
 
35
+ # LLaVA importları
36
  from llava.conversation import conv_templates
37
  from llava.constants import (
38
  DEFAULT_IMAGE_TOKEN,
 
45
  # (gerekirse fallback için)
46
  try:
47
  from transformers import AutoProcessor, CLIPImageProcessor # type: ignore
48
+ print("[DEBUG] transformers AutoProcessor/CLIPImageProcessor available.")
49
+ except Exception as e:
50
  AutoProcessor = None
51
  CLIPImageProcessor = None
52
+ print(f"[DEBUG] transformers processors import failed: {e}")
53
 
54
  # ---------- yardımcılar ----------
55
  def _get_env(name: str, default: Optional[str] = None) -> Optional[str]:
 
57
  return v if v not in (None, "") else default
58
 
59
  def _pick_device() -> torch.device:
60
+ dev = torch.device("cpu")
61
  if torch.cuda.is_available():
62
+ dev = torch.device("cuda")
63
+ elif torch.backends.mps.is_available():
64
+ dev = torch.device("mps")
65
+ print(f"[DEBUG] pick_device -> {dev}")
66
+ return dev
67
 
68
  def _pick_dtype(device: torch.device):
69
  if device.type == "cuda":
70
+ dt = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
71
+ else:
72
+ dt = torch.float32
73
+ print(f"[DEBUG] pick_dtype({device}) -> {dt}")
74
+ return dt
75
 
76
  def _is_probably_base64(s: str) -> bool:
77
  s = s.strip()
 
81
  return len(s) % 4 == 0 and all(c in allowed for c in s)
82
 
83
  def _load_image_from_any(image_input: Any) -> Image.Image:
84
+ print(f"[DEBUG] _load_image_from_any type={type(image_input)}")
85
  if isinstance(image_input, Image.Image):
86
+ print("[DEBUG] image_input is PIL.Image")
87
  return image_input.convert("RGB")
88
  if isinstance(image_input, (bytes, bytearray)):
89
+ print("[DEBUG] image_input is raw bytes")
90
  return Image.open(io.BytesIO(image_input)).convert("RGB")
91
  if hasattr(image_input, "read"):
92
+ print("[DEBUG] image_input is file-like")
93
  return Image.open(image_input).convert("RGB")
94
  if isinstance(image_input, str):
95
  s = image_input.strip()
96
  # data URL
97
  if s.startswith("data:image"):
98
+ print("[DEBUG] image_input is data URL")
99
+ try:
100
+ _, b64 = s.split(",", 1)
101
+ data = base64.b64decode(b64)
102
+ return Image.open(io.BytesIO(data)).convert("RGB")
103
+ except Exception as e:
104
+ print(f"[DEBUG] data URL decode failed: {e}")
105
+ raise
106
+ # raw base64
107
  if _is_probably_base64(s) and not s.startswith(("http://", "https://")):
108
+ print("[DEBUG] image_input looks like raw base64")
109
  try:
110
  data = base64.b64decode(s)
111
  return Image.open(io.BytesIO(data)).convert("RGB")
112
+ except Exception as e:
113
+ print(f"[DEBUG] raw base64 decode failed: {e} (fall through)")
114
  # URL
115
  if s.startswith(("http://", "https://")):
116
+ print(f"[DEBUG] image_input is URL: {s}")
117
  resp = requests.get(s, timeout=20)
118
  resp.raise_for_status()
119
  return Image.open(io.BytesIO(resp.content)).convert("RGB")
120
  # path
121
+ print(f"[DEBUG] image_input is path: {s}")
122
  return Image.open(s).convert("RGB")
123
  raise ValueError(f"Unsupported image input type: {type(image_input)}")
124
 
 
144
  return f"{token}\n{prompt}"
145
  return f"{DEFAULT_IMAGE_TOKEN}\n{prompt}"
146
 
 
147
  def _maybe_get_vision_tower_from_cfg(cfg) -> Optional[str]:
148
  vt = getattr(cfg, "vision_tower", None)
149
  if isinstance(vt, (list, tuple)) and vt:
 
158
  self.proc = proc
159
  def preprocess(self, image, return_tensors="pt"):
160
  out = self.proc(image, return_tensors=return_tensors)
 
161
  if isinstance(out, dict):
162
  return out
163
  return {"pixel_values": out}
164
 
165
  def _ensure_image_processor(image_processor, model_cfg, model_path: str):
166
+ print(f"[DEBUG] _ensure_image_processor entry; got={type(image_processor)}")
167
  if image_processor is not None:
 
168
  if hasattr(image_processor, "preprocess"):
169
+ print("[DEBUG] existing image_processor has preprocess()")
170
  return image_processor
171
  if hasattr(image_processor, "image_processor"):
172
  ip = image_processor.image_processor
173
+ print(f"[DEBUG] using image_processor.image_processor -> {type(ip)}")
174
  if hasattr(ip, "preprocess"):
175
  return ip
176
  return _ProcessorWrapper(ip)
177
  if callable(image_processor):
178
+ print("[DEBUG] wrapping callable image_processor")
179
  return _ProcessorWrapper(image_processor)
180
 
 
181
  if AutoProcessor is not None:
182
  try:
183
+ print(f"[DEBUG] AutoProcessor.from_pretrained({model_path}) ...")
184
  proc = AutoProcessor.from_pretrained(model_path, trust_remote_code=True)
185
  if hasattr(proc, "preprocess"):
186
+ print("[DEBUG] AutoProcessor has preprocess()")
187
  return proc
188
  if hasattr(proc, "image_processor"):
189
  ip = proc.image_processor
190
+ print("[DEBUG] AutoProcessor.image_processor found")
191
  if hasattr(ip, "preprocess"):
192
  return ip
193
  return _ProcessorWrapper(ip)
194
+ print("[DEBUG] wrapping AutoProcessor with _ProcessorWrapper")
195
  return _ProcessorWrapper(proc)
196
+ except Exception as e:
197
+ print(f"[DEBUG] AutoProcessor failed: {e}")
198
 
 
199
  vt = _maybe_get_vision_tower_from_cfg(model_cfg)
200
+ print(f"[DEBUG] vision_tower from cfg/env -> {vt}")
201
  if vt and CLIPImageProcessor is not None:
202
  try:
203
  ip = CLIPImageProcessor.from_pretrained(vt)
204
+ print("[DEBUG] CLIPImageProcessor created from vision_tower")
205
  return ip
206
+ except Exception as e:
207
+ print(f"[DEBUG] CLIPImageProcessor failed: {e}")
208
 
209
+ print("[DEBUG] image_processor could not be resolved -> None")
210
  return None
211
 
212
  # ---------- Endpoint Handler ----------
213
  class EndpointHandler:
 
 
 
 
 
 
 
214
  def __init__(self, model_dir: Optional[str] = None):
215
+ print(f"[DEBUG] EndpointHandler.__init__ model_dir={model_dir}")
216
  self.model_dir = model_dir
217
  self.model = None
218
  self.tokenizer = None
 
225
  def load(self):
226
  model_path = _get_env("HF_MODEL_ID", "PULSE-ECG/PULSE-7B")
227
  model_base = _get_env("HF_MODEL_BASE", None)
228
+ print(f"[DEBUG] load(): HF_MODEL_ID={model_path}, HF_MODEL_BASE={model_base}")
229
 
230
  os.environ.setdefault("ATTN_IMPLEMENTATION", "flash_attention_2")
231
  os.environ.setdefault("FLASH_ATTENTION", "1")
232
+ print(f"[DEBUG] env ATTN_IMPLEMENTATION={os.getenv('ATTN_IMPLEMENTATION')} FLASH_ATTENTION={os.getenv('FLASH_ATTENTION')}")
233
 
234
+ print("[DEBUG] calling load_pretrained_model ...")
235
  self.tokenizer, self.model, self.image_processor, self.context_len = load_pretrained_model(
236
  model_path=model_path,
237
  model_base=model_base,
 
241
  device=self.device,
242
  )
243
  self.model_name = getattr(self.model.config, "name_or_path", str(model_path))
244
+ print(f"[DEBUG] model loaded: name={self.model_name}")
245
+ print(f"[DEBUG] tokenizer={type(self.tokenizer)}, model={type(self.model)}, image_processor={type(self.image_processor)}, context_len={self.context_len}")
246
 
247
+ # image_processor fallback
248
  self.image_processor = _ensure_image_processor(self.image_processor, self.model.config, model_path)
249
+ print(f"[DEBUG] final image_processor -> {type(self.image_processor)}")
250
 
251
  # tokenizer güvenliği
252
  try:
253
  self.tokenizer.padding_side = "left"
254
  if self.tokenizer.pad_token_id is None:
255
  self.tokenizer.pad_token_id = self.tokenizer.eos_token_id
256
+ print(f"[DEBUG] tokenizer.padding_side={self.tokenizer.padding_side}, pad_token_id={self.tokenizer.pad_token_id}")
257
+ except Exception as e:
258
+ print(f"[DEBUG] tokenizer safety failed: {e}")
259
 
260
  self.model.eval()
261
+ print("[DEBUG] model.eval() done")
262
  return True
263
 
264
  @torch.inference_mode()
265
  def __call__(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
266
+ print(f"[DEBUG] __call__ inputs type={type(inputs)} keys={list(inputs.keys()) if hasattr(inputs,'keys') else 'N/A'}")
267
+ # HF bazen {"inputs": {...}} sarmalar
268
  if "inputs" in inputs and isinstance(inputs["inputs"], dict):
269
+ print("[DEBUG] unwrapping inputs['inputs']")
270
  inputs = inputs["inputs"]
271
 
272
+ print(f"[DEBUG] after unwrap keys={list(inputs.keys()) if hasattr(inputs,'keys') else 'N/A'}")
273
  prompt = inputs.get("query") or inputs.get("prompt") or inputs.get("istem") or ""
274
  image_in = inputs.get("image") or inputs.get("image_url") or inputs.get("img")
275
+ print(f"[DEBUG] prompt exists={bool(prompt.strip())}, image_in is None={image_in is None}")
276
+
277
  if not image_in:
278
  return {"error": "Missing 'image' in payload"}
279
  if not isinstance(prompt, str) or not prompt.strip():
280
  return {"error": "Missing 'query'/'prompt' text"}
281
 
282
+ try:
283
+ temperature = float(inputs.get("temperature", 0.0))
284
+ top_p = float(inputs.get("top_p", 0.9))
285
+ max_new = int(inputs.get("max_new_tokens", inputs.get("max_tokens", 512)))
286
+ repetition_penalty = float(inputs.get("repetition_penalty", 1.0))
287
+ conv_mode_override = inputs.get("conv_mode") or _get_env("CONV_MODE", None)
288
+ except Exception as e:
289
+ print(f"[DEBUG] param parse failed: {e}")
290
+ return {"error": f"Bad parameters: {e}"}
291
 
292
  # ---- görüntü
293
  try:
294
  image = _load_image_from_any(image_in)
295
+ print(f"[DEBUG] loaded image size={image.size}")
296
  except Exception as e:
297
+ print(f"[DEBUG] image load failed: {e}")
298
  return {"error": f"Failed to load image: {e}"}
299
+
300
  images = [image]
301
  image_sizes = [image.size]
302
+ print(f"[DEBUG] image_sizes={image_sizes}; device={self.device}, dtype={self.dtype}")
303
 
304
+ # process_images -> tensör
305
  try:
306
  if self.image_processor is None:
307
+ raise RuntimeError("image_processor is None before process_images")
 
308
  images_tensor = process_images(images, self.image_processor, self.model.config)
309
+ print(f"[DEBUG] process_images OK; type={type(images_tensor)} shape={getattr(images_tensor,'shape',None)}")
310
+ except Exception as e:
311
+ print(f"[DEBUG] process_images failed: {e} -> fallback to preprocess()")
312
+ try:
313
+ if hasattr(self.image_processor, "preprocess"):
314
+ out = self.image_processor.preprocess(image, return_tensors="pt")
315
+ images_tensor = out["pixel_values"]
316
+ print(f"[DEBUG] preprocess() OK; shape={getattr(images_tensor,'shape',None)}")
317
+ else:
318
+ # en kaba yedek
319
+ wrp = _ProcessorWrapper(self.image_processor)
320
+ out = wrp.preprocess(image, return_tensors="pt")
321
+ images_tensor = out["pixel_values"]
322
+ print(f"[DEBUG] _ProcessorWrapper OK; shape={getattr(images_tensor,'shape',None)}")
323
+ except Exception as e2:
324
+ print(f"[DEBUG] preprocess fallback failed: {e2}")
325
+ return {"error": f"Image processing failed: {e} / {e2}"}
326
+
327
+ try:
328
+ images_tensor = images_tensor.to(self.device, dtype=self.dtype)
329
+ print(f"[DEBUG] images_tensor moved to {self.device} dtype={self.dtype}")
330
+ except Exception as e:
331
+ print(f"[DEBUG] images_tensor.to failed: {e}")
332
+ return {"error": f"Tensor move/cast failed: {e}"}
333
 
334
  # ---- konuşma şablonu + prompt
335
  mode = conv_mode_override or _get_conv_mode(self.model_name)
336
  conv = (conv_templates.get(mode) or conv_templates[list(conv_templates.keys())[0]]).copy()
337
+ print(f"[DEBUG] conv_mode={mode}; conv.roles={conv.roles}")
338
 
339
  formatted_prompt = _build_prompt_with_image(prompt.strip(), self.model.config)
340
+ print(f"[DEBUG] formatted_prompt startswith image token? {formatted_prompt.startswith(DEFAULT_IMAGE_TOKEN) or formatted_prompt.startswith(DEFAULT_IM_START_TOKEN)}")
341
  conv.append_message(conv.roles[0], formatted_prompt)
342
  conv.append_message(conv.roles[1], None)
343
  full_prompt = conv.get_prompt()
344
+ print(f"[DEBUG] full_prompt length={len(full_prompt)}")
345
 
346
+ # ---- tokenizasyon
347
  try:
348
  input_ids = tokenizer_image_token(
349
  full_prompt, self.tokenizer, image_token_index=-200, return_tensors="pt"
350
  ).unsqueeze(0).to(self.device)
351
+ print(f"[DEBUG] tokenizer_image_token OK; input_ids.shape={input_ids.shape}")
352
+ except Exception as e:
353
+ print(f"[DEBUG] tokenizer_image_token failed: {e}; fallback to plain tokenizer")
354
+ try:
355
+ toks = self.tokenizer([full_prompt], return_tensors="pt", padding=True, truncation=True)
356
+ input_ids = toks["input_ids"].to(self.device)
357
+ print(f"[DEBUG] plain tokenizer OK; input_ids.shape={input_ids.shape}")
358
+ except Exception as e2:
359
+ print(f"[DEBUG] plain tokenizer failed: {e2}")
360
+ return {"error": f"Tokenization failed: {e} / {e2}"}
361
 
362
  attention_mask = torch.ones_like(input_ids, device=self.device)
363
+ print(f"[DEBUG] attention_mask shape={attention_mask.shape}")
364
 
365
  # ---- generate
366
  try:
367
+ print(f"[DEBUG] calling model.generate(...) max_new_tokens={max_new} temp={temperature} top_p={top_p} rep={repetition_penalty}")
368
  gen_ids = self.model.generate(
369
  input_ids=input_ids,
370
  attention_mask=attention_mask,
 
377
  repetition_penalty=repetition_penalty,
378
  use_cache=True,
379
  )
380
+ print(f"[DEBUG] generate OK; gen_ids.shape={gen_ids.shape}")
381
  except Exception as e:
382
+ print(f"[DEBUG] generate failed: {e}")
383
  return {"error": f"Generation failed: {e}"}
384
 
385
+ # ---- decode (echo-fix)
386
+ try:
387
+ new_tokens = gen_ids[0, input_ids.shape[1]:]
388
+ text = self.tokenizer.decode(new_tokens, skip_special_tokens=True).strip()
389
+ print(f"[DEBUG] decoded text length={len(text)}")
390
+ except Exception as e:
391
+ print(f"[DEBUG] decode failed: {e}")
392
+ return {"error": f"Decode failed: {e}"}
393
 
394
  return {
395
  "generated_text": text,