ubden commited on
Commit
f5391c9
·
verified ·
1 Parent(s): 8433be8

Upload 14 files

Browse files

major update with deepseek integration

Files changed (6) hide show
  1. deployment_guide.md +133 -0
  2. generation_config.json +43 -2
  3. handler.py +162 -13
  4. requirements.txt +17 -1
  5. test_requests.json +220 -142
  6. utils.py +360 -0
deployment_guide.md ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PULSE-7B Handler Deployment Guide
2
+
3
+ ## 🚀 Deployment Rehberi
4
+
5
+ ### Gereksinimler
6
+ - Python 3.8+
7
+ - CUDA 11.8+ (GPU kullanımı için)
8
+ - Minimum 16GB RAM (CPU), 8GB VRAM (GPU)
9
+
10
+ ### Kurulum
11
+
12
+ 1. **Bağımlılıkları yükleyin:**
13
+ ```bash
14
+ pip install -r requirements.txt
15
+ ```
16
+
17
+ 2. **Flash Attention (isteğe bağlı, performans için):**
18
+ ```bash
19
+ pip install flash-attn --no-build-isolation
20
+ ```
21
+
22
+ ### HuggingFace Inference Deployment
23
+
24
+ #### 1. Model Repository Yapısı
25
+ ```
26
+ your-model-repo/
27
+ ├── handler.py
28
+ ├── config.json
29
+ ├── generation_config.json
30
+ ├── requirements.txt
31
+ ├── model.safetensors.index.json
32
+ ├── tokenizer_config.json
33
+ ├── special_tokens_map.json
34
+ └── tokenizer.model
35
+ ```
36
+
37
+ #### 2. Endpoint Oluşturma
38
+ ```bash
39
+ # HuggingFace CLI ile deploy
40
+ huggingface-cli login
41
+ huggingface-cli repo create your-pulse-endpoint --type=space
42
+ ```
43
+
44
+ #### 3. Test Requests
45
+
46
+ **Image URL ile test:**
47
+ ```bash
48
+ curl -X POST "YOUR_ENDPOINT_URL" \
49
+ -H "Content-Type: application/json" \
50
+ -d '{
51
+ "inputs": {
52
+ "query": "Analyze this ECG image",
53
+ "image": "https://i.imgur.com/7uuejqO.jpeg"
54
+ },
55
+ "parameters": {
56
+ "temperature": 0.2,
57
+ "max_new_tokens": 512
58
+ }
59
+ }'
60
+ ```
61
+
62
+ **Base64 ile test:**
63
+ ```bash
64
+ curl -X POST "YOUR_ENDPOINT_URL" \
65
+ -H "Content-Type: application/json" \
66
+ -d '{
67
+ "inputs": {
68
+ "query": "What do you see in this ECG?",
69
+ "image": "data:image/jpeg;base64,/9j/4AAQ..."
70
+ },
71
+ "parameters": {
72
+ "temperature": 0.2
73
+ }
74
+ }'
75
+ ```
76
+
77
+ ### Performans Optimizasyonları
78
+
79
+ #### GPU Memory Optimizasyonu
80
+ - `torch_dtype=torch.bfloat16` kullanın
81
+ - `low_cpu_mem_usage=True` ayarlayın
82
+ - `device_map="auto"` ile otomatik dağıtım
83
+
84
+ #### CPU Optimizasyonu
85
+ - `torch_dtype=torch.float32` kullanın
86
+ - Thread sayısını ayarlayın: `torch.set_num_threads(4)`
87
+
88
+ ### Monitoring ve Debugging
89
+
90
+ #### Log Seviyeleri
91
+ ```python
92
+ import logging
93
+ logging.basicConfig(level=logging.INFO)
94
+ ```
95
+
96
+ #### Memory Usage
97
+ ```python
98
+ import torch
99
+ print(f"GPU Memory: {torch.cuda.memory_allocated()/1024**3:.2f}GB")
100
+ ```
101
+
102
+ ### Troubleshooting
103
+
104
+ #### Common Issues:
105
+
106
+ 1. **CUDA Out of Memory**
107
+ - Batch size'ı azaltın
108
+ - `max_new_tokens` değerini düşürün
109
+ - Gradient checkpointing kullanın
110
+
111
+ 2. **Slow Image Processing**
112
+ - Image timeout değerini artırın
113
+ - Image resize threshold ayarlayın
114
+
115
+ 3. **Model Loading Issues**
116
+ - HuggingFace token'ını kontrol edin
117
+ - Network bağlantısını doğrulayın
118
+ - Cache dizinini temizleyin
119
+
120
+ ### Security Best Practices
121
+
122
+ - Image URL'leri validate edin
123
+ - Base64 boyut limitlerini ayarlayın
124
+ - Rate limiting uygulayın
125
+ - Input sanitization yapın
126
+
127
+ ### Monitoring Metrics
128
+
129
+ - Response time
130
+ - Memory usage
131
+ - Error rates
132
+ - Image processing success rate
133
+ - Token generation speed
generation_config.json CHANGED
@@ -1,8 +1,49 @@
1
  {
 
2
  "attn_implementation": "flash_attention_2",
3
  "bos_token_id": 1,
4
  "eos_token_id": 2,
5
- "max_length": 4096,
6
  "pad_token_id": 0,
7
- "transformers_version": "4.37.2"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  }
 
1
  {
2
+ "_from_model_config": true,
3
  "attn_implementation": "flash_attention_2",
4
  "bos_token_id": 1,
5
  "eos_token_id": 2,
 
6
  "pad_token_id": 0,
7
+ "max_length": 4096,
8
+ "max_new_tokens": 2048,
9
+ "min_length": 1,
10
+ "min_new_tokens": 1,
11
+ "early_stopping": false,
12
+ "max_time": null,
13
+ "do_sample": true,
14
+ "num_beams": 1,
15
+ "num_beam_groups": 1,
16
+ "penalty_alpha": null,
17
+ "use_cache": true,
18
+ "temperature": 0.2,
19
+ "top_k": 50,
20
+ "top_p": 0.9,
21
+ "typical_p": 1.0,
22
+ "epsilon_cutoff": 0.0,
23
+ "eta_cutoff": 0.0,
24
+ "diversity_penalty": 0.0,
25
+ "repetition_penalty": 1.05,
26
+ "encoder_repetition_penalty": 1.0,
27
+ "length_penalty": 1.0,
28
+ "no_repeat_ngram_size": 0,
29
+ "bad_words_ids": null,
30
+ "force_words_ids": null,
31
+ "renormalize_logits": false,
32
+ "constraints": null,
33
+ "forced_bos_token_id": null,
34
+ "forced_eos_token_id": null,
35
+ "remove_invalid_values": false,
36
+ "exponential_decay_length_penalty": null,
37
+ "suppress_tokens": null,
38
+ "begin_suppress_tokens": null,
39
+ "forced_decoder_ids": null,
40
+ "sequence_bias": null,
41
+ "guidance_scale": null,
42
+ "low_memory": null,
43
+ "num_return_sequences": 1,
44
+ "output_attentions": false,
45
+ "output_hidden_states": false,
46
+ "output_scores": false,
47
+ "return_dict_in_generate": false,
48
+ "transformers_version": "4.40.0"
49
  }
handler.py CHANGED
@@ -10,6 +10,23 @@ import base64
10
  from io import BytesIO
11
  from PIL import Image
12
  import requests
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
 
15
  class EndpointHandler:
@@ -171,6 +188,56 @@ class EndpointHandler:
171
 
172
  return None
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  def __call__(self, data: Dict[str, Any]) -> List[Dict[str, Any]]:
175
  """
176
  Main processing function - where the magic happens!
@@ -189,6 +256,12 @@ class EndpointHandler:
189
  "handler": "Ubden® Team Enhanced Handler"
190
  }]
191
 
 
 
 
 
 
 
192
  try:
193
  # Parse the inputs - flexible format support
194
  inputs = data.get("inputs", "")
@@ -203,7 +276,20 @@ class EndpointHandler:
203
  # Check for image in various formats
204
  image_input = inputs.get("image", inputs.get("image_url", inputs.get("image_base64", None)))
205
  if image_input:
 
 
 
 
 
 
 
 
 
 
 
206
  image = self.process_image_input(image_input)
 
 
207
  if image:
208
  print(f"✅ Image processed successfully: {image.size[0]}x{image.size[1]} pixels")
209
  # Add image context to the prompt for better processing
@@ -220,13 +306,29 @@ class EndpointHandler:
220
 
221
  # Get generation parameters with sensible defaults
222
  parameters = data.get("parameters", {})
223
- max_new_tokens = min(parameters.get("max_new_tokens", 512), 2048) # Increased default
224
- temperature = max(0.01, min(parameters.get("temperature", 0.2), 2.0)) # Clamp temperature
225
- top_p = max(0.01, min(parameters.get("top_p", 0.9), 1.0)) # Clamp top_p
226
- do_sample = parameters.get("do_sample", temperature > 0.01) # Auto-set based on temperature
227
- repetition_penalty = max(1.0, min(parameters.get("repetition_penalty", 1.05), 2.0)) # Clamp penalty
228
- stop_sequences = parameters.get("stop", ["</s>"]) # Support stop sequences
229
- return_full_text = parameters.get("return_full_text", False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
  print(f"🎛️ Generation params: max_tokens={max_new_tokens}, temp={temperature}, top_p={top_p}, rep_penalty={repetition_penalty}")
232
 
@@ -255,17 +357,43 @@ class EndpointHandler:
255
  if generated_text.endswith(stop_seq):
256
  generated_text = generated_text[:-len(stop_seq)].rstrip()
257
 
258
- return [{
 
259
  "generated_text": generated_text,
260
  "model": "PULSE-7B",
261
  "processing_method": "pipeline"
262
- }]
 
 
 
 
 
 
 
 
 
 
 
 
263
  else:
264
- return [{
 
265
  "generated_text": str(result),
266
  "model": "PULSE-7B",
267
  "processing_method": "pipeline"
268
- }]
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
  # Manual generation mode
271
  else:
@@ -323,15 +451,36 @@ class EndpointHandler:
323
  if generated_text.endswith(stop_seq):
324
  generated_text = generated_text[:-len(stop_seq)].rstrip()
325
 
326
- return [{
 
327
  "generated_text": generated_text.strip(),
328
  "model": "PULSE-7B",
329
  "processing_method": "manual"
330
- }]
 
 
 
 
 
 
 
 
 
 
 
 
331
 
332
  except Exception as e:
333
  error_msg = f"Generation error: {str(e)}"
334
  print(f"❌ {error_msg}")
 
 
 
 
 
 
 
 
335
  return [{
336
  "generated_text": "",
337
  "error": error_msg,
 
10
  from io import BytesIO
11
  from PIL import Image
12
  import requests
13
+ import time
14
+
15
+ # Import utilities if available
16
+ try:
17
+ from utils import (
18
+ performance_monitor,
19
+ validate_image_input,
20
+ sanitize_parameters,
21
+ get_system_info,
22
+ create_health_check,
23
+ deepseek_client
24
+ )
25
+ UTILS_AVAILABLE = True
26
+ except ImportError:
27
+ UTILS_AVAILABLE = False
28
+ deepseek_client = None
29
+ print("⚠️ Utils module not found - performance monitoring and DeepSeek integration disabled")
30
 
31
 
32
  class EndpointHandler:
 
188
 
189
  return None
190
 
191
+ def add_turkish_commentary(self, response: Dict[str, Any], enable_commentary: bool, timeout: int = 30) -> Dict[str, Any]:
192
+ """Add Turkish commentary to the response using DeepSeek API"""
193
+ if not enable_commentary:
194
+ return response
195
+
196
+ if not UTILS_AVAILABLE or not deepseek_client:
197
+ print("⚠️ DeepSeek client not available - skipping Turkish commentary")
198
+ response["commentary_status"] = "unavailable"
199
+ return response
200
+
201
+ if not deepseek_client.is_available():
202
+ print("⚠️ DeepSeek API key not configured - skipping Turkish commentary")
203
+ response["commentary_status"] = "api_key_missing"
204
+ return response
205
+
206
+ generated_text = response.get("generated_text", "")
207
+ if not generated_text:
208
+ print("⚠️ No generated text to comment on")
209
+ response["commentary_status"] = "no_text"
210
+ return response
211
+
212
+ print("🔄 DeepSeek ile Türkçe yorum ekleniyor...")
213
+ commentary_result = deepseek_client.get_turkish_commentary(generated_text, timeout)
214
+
215
+ if commentary_result["success"]:
216
+ response["comment_text"] = commentary_result["comment_text"]
217
+ response["commentary_model"] = commentary_result.get("model", "deepseek-chat")
218
+ response["commentary_tokens"] = commentary_result.get("tokens_used", 0)
219
+ response["commentary_status"] = "success"
220
+ print("✅ Türkçe yorum başarıyla eklendi")
221
+ else:
222
+ response["comment_text"] = ""
223
+ response["commentary_error"] = commentary_result["error"]
224
+ response["commentary_status"] = "failed"
225
+ print(f"❌ Türkçe yorum eklenemedi: {commentary_result['error']}")
226
+
227
+ return response
228
+
229
+ def health_check(self) -> Dict[str, Any]:
230
+ """Health check endpoint"""
231
+ if UTILS_AVAILABLE:
232
+ return create_health_check()
233
+ else:
234
+ return {
235
+ 'status': 'healthy',
236
+ 'model': 'PULSE-7B',
237
+ 'timestamp': time.time(),
238
+ 'handler_version': '2.0.0'
239
+ }
240
+
241
  def __call__(self, data: Dict[str, Any]) -> List[Dict[str, Any]]:
242
  """
243
  Main processing function - where the magic happens!
 
256
  "handler": "Ubden® Team Enhanced Handler"
257
  }]
258
 
259
+ # Performance monitoring
260
+ start_time = time.time()
261
+ request_type = "text_only"
262
+ success = False
263
+ image_processing_time = 0.0
264
+
265
  try:
266
  # Parse the inputs - flexible format support
267
  inputs = data.get("inputs", "")
 
276
  # Check for image in various formats
277
  image_input = inputs.get("image", inputs.get("image_url", inputs.get("image_base64", None)))
278
  if image_input:
279
+ # Determine request type and validate input
280
+ if UTILS_AVAILABLE:
281
+ validation = validate_image_input(image_input)
282
+ request_type = validation.get('type', 'unknown')
283
+ if request_type == 'url':
284
+ request_type = 'image_url'
285
+ else:
286
+ request_type = 'image_url' if image_input.startswith(('http://', 'https://')) else 'base64'
287
+
288
+ # Process image with timing
289
+ image_start = time.time()
290
  image = self.process_image_input(image_input)
291
+ image_processing_time = time.time() - image_start
292
+
293
  if image:
294
  print(f"✅ Image processed successfully: {image.size[0]}x{image.size[1]} pixels")
295
  # Add image context to the prompt for better processing
 
306
 
307
  # Get generation parameters with sensible defaults
308
  parameters = data.get("parameters", {})
309
+
310
+ # Check if Turkish commentary is requested
311
+ enable_turkish_commentary = parameters.get("enable_turkish_commentary", True) # Default true
312
+ deepseek_timeout = parameters.get("deepseek_timeout", 30)
313
+
314
+ # Use utils for parameter sanitization if available
315
+ if UTILS_AVAILABLE:
316
+ sanitized_params = sanitize_parameters(parameters)
317
+ max_new_tokens = sanitized_params["max_new_tokens"]
318
+ temperature = sanitized_params["temperature"]
319
+ top_p = sanitized_params["top_p"]
320
+ repetition_penalty = sanitized_params["repetition_penalty"]
321
+ stop_sequences = sanitized_params["stop"]
322
+ return_full_text = sanitized_params["return_full_text"]
323
+ do_sample = sanitized_params["do_sample"]
324
+ else:
325
+ max_new_tokens = min(parameters.get("max_new_tokens", 512), 2048)
326
+ temperature = max(0.01, min(parameters.get("temperature", 0.2), 2.0))
327
+ top_p = max(0.01, min(parameters.get("top_p", 0.9), 1.0))
328
+ do_sample = parameters.get("do_sample", temperature > 0.01)
329
+ repetition_penalty = max(1.0, min(parameters.get("repetition_penalty", 1.05), 2.0))
330
+ stop_sequences = parameters.get("stop", ["</s>"])
331
+ return_full_text = parameters.get("return_full_text", False)
332
 
333
  print(f"🎛️ Generation params: max_tokens={max_new_tokens}, temp={temperature}, top_p={top_p}, rep_penalty={repetition_penalty}")
334
 
 
357
  if generated_text.endswith(stop_seq):
358
  generated_text = generated_text[:-len(stop_seq)].rstrip()
359
 
360
+ success = True
361
+ result = {
362
  "generated_text": generated_text,
363
  "model": "PULSE-7B",
364
  "processing_method": "pipeline"
365
+ }
366
+
367
+ # Add Turkish commentary if requested
368
+ result = self.add_turkish_commentary(result, enable_turkish_commentary, deepseek_timeout)
369
+
370
+ # Log performance metrics
371
+ if UTILS_AVAILABLE:
372
+ generation_time = time.time() - start_time
373
+ performance_monitor.log_request(
374
+ request_type, success, generation_time, image_processing_time
375
+ )
376
+
377
+ return [result]
378
  else:
379
+ success = True
380
+ result_dict = {
381
  "generated_text": str(result),
382
  "model": "PULSE-7B",
383
  "processing_method": "pipeline"
384
+ }
385
+
386
+ # Add Turkish commentary if requested
387
+ result_dict = self.add_turkish_commentary(result_dict, enable_turkish_commentary, deepseek_timeout)
388
+
389
+ # Log performance metrics
390
+ if UTILS_AVAILABLE:
391
+ generation_time = time.time() - start_time
392
+ performance_monitor.log_request(
393
+ request_type, success, generation_time, image_processing_time
394
+ )
395
+
396
+ return [result_dict]
397
 
398
  # Manual generation mode
399
  else:
 
451
  if generated_text.endswith(stop_seq):
452
  generated_text = generated_text[:-len(stop_seq)].rstrip()
453
 
454
+ success = True
455
+ result = {
456
  "generated_text": generated_text.strip(),
457
  "model": "PULSE-7B",
458
  "processing_method": "manual"
459
+ }
460
+
461
+ # Add Turkish commentary if requested
462
+ result = self.add_turkish_commentary(result, enable_turkish_commentary, deepseek_timeout)
463
+
464
+ # Log performance metrics
465
+ if UTILS_AVAILABLE:
466
+ generation_time = time.time() - start_time
467
+ performance_monitor.log_request(
468
+ request_type, success, generation_time, image_processing_time
469
+ )
470
+
471
+ return [result]
472
 
473
  except Exception as e:
474
  error_msg = f"Generation error: {str(e)}"
475
  print(f"❌ {error_msg}")
476
+
477
+ # Log failed request
478
+ if UTILS_AVAILABLE:
479
+ generation_time = time.time() - start_time
480
+ performance_monitor.log_request(
481
+ request_type, success, generation_time, image_processing_time
482
+ )
483
+
484
  return [{
485
  "generated_text": "",
486
  "error": error_msg,
requirements.txt CHANGED
@@ -3,4 +3,20 @@ torch>=2.1.0
3
  accelerate>=0.25.0
4
  sentencepiece
5
  safetensors
6
- protobuf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  accelerate>=0.25.0
4
  sentencepiece
5
  safetensors
6
+ protobuf
7
+
8
+ # Image processing dependencies
9
+ Pillow>=9.0.0
10
+ requests>=2.28.0
11
+
12
+ # Optional performance improvements
13
+ flash-attn>=2.0.0; sys_platform != "darwin"
14
+ bitsandbytes>=0.41.0; sys_platform != "darwin"
15
+
16
+ # Utility libraries
17
+ numpy>=1.21.0
18
+ typing-extensions>=4.0.0
19
+ psutil>=5.8.0
20
+
21
+ # HuggingFace Inference specific
22
+ huggingface-hub>=0.16.0
test_requests.json CHANGED
@@ -1,142 +1,220 @@
1
- {
2
- "test_requests": {
3
- "image_url_request": {
4
- "description": "ECG analizi için image URL ile istek",
5
- "request": {
6
- "inputs": {
7
- "query": "What are the main features and diagnosis in this ECG image? Provide a concise, clinical answer.",
8
- "image": "https://i.imgur.com/7uuejqO.jpeg"
9
- },
10
- "parameters": {
11
- "max_new_tokens": 512,
12
- "temperature": 0.2,
13
- "top_p": 0.9,
14
- "repetition_penalty": 1.05,
15
- "stop": ["</s>"],
16
- "return_full_text": false
17
- }
18
- }
19
- },
20
-
21
- "base64_request": {
22
- "description": "ECG analizi için base64 encoded image ile istek",
23
- "request": {
24
- "inputs": {
25
- "query": "What are the main features and diagnosis in this ECG image? Provide a concise, clinical answer.",
26
- "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
27
- },
28
- "parameters": {
29
- "max_new_tokens": 512,
30
- "temperature": 0.2,
31
- "top_p": 0.9,
32
- "repetition_penalty": 1.05,
33
- "stop": ["</s>"],
34
- "return_full_text": false
35
- }
36
- }
37
- },
38
-
39
- "minimal_url_request": {
40
- "description": "Minimal parametrelerle image URL isteği",
41
- "request": {
42
- "inputs": {
43
- "query": "Analyze this ECG image.",
44
- "image": "https://i.imgur.com/7uuejqO.jpeg"
45
- },
46
- "parameters": {
47
- "temperature": 0.2
48
- }
49
- }
50
- },
51
-
52
- "minimal_base64_request": {
53
- "description": "Minimal parametrelerle base64 isteği",
54
- "request": {
55
- "inputs": {
56
- "query": "Analyze this ECG image.",
57
- "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
58
- },
59
- "parameters": {
60
- "temperature": 0.2
61
- }
62
- }
63
- },
64
-
65
- "text_only_request": {
66
- "description": "Sadece text ile istek (image olmadan)",
67
- "request": {
68
- "inputs": {
69
- "query": "What are the common causes of atrial fibrillation?"
70
- },
71
- "parameters": {
72
- "max_new_tokens": 256,
73
- "temperature": 0.3,
74
- "top_p": 0.9
75
- }
76
- }
77
- },
78
-
79
- "advanced_parameters_request": {
80
- "description": "Gelişmiş parametrelerle image URL isteği",
81
- "request": {
82
- "inputs": {
83
- "query": "Provide detailed ECG analysis including rhythm, rate, intervals, and potential abnormalities.",
84
- "image": "https://i.imgur.com/7uuejqO.jpeg"
85
- },
86
- "parameters": {
87
- "max_new_tokens": 1024,
88
- "temperature": 0.1,
89
- "top_p": 0.95,
90
- "repetition_penalty": 1.1,
91
- "stop": ["</s>", "\n\n"],
92
- "return_full_text": false,
93
- "do_sample": true
94
- }
95
- }
96
- }
97
- },
98
-
99
- "expected_response_format": {
100
- "description": "Beklenen response formatı",
101
- "format": [
102
- {
103
- "generated_text": "ECG analysis response text here...",
104
- "model": "PULSE-7B",
105
- "processing_method": "pipeline"
106
- }
107
- ]
108
- },
109
-
110
- "error_response_format": {
111
- "description": "Hata durumunda response formatı",
112
- "format": [
113
- {
114
- "generated_text": "",
115
- "error": "Error message here...",
116
- "model": "PULSE-7B",
117
- "handler": "Ubden® Team Enhanced Handler",
118
- "success": false
119
- }
120
- ]
121
- },
122
-
123
- "usage_notes": {
124
- "supported_image_formats": [
125
- "JPEG", "PNG", "GIF", "BMP", "WebP"
126
- ],
127
- "supported_input_methods": [
128
- "HTTP/HTTPS URL",
129
- "Base64 encoded with data URI prefix",
130
- "Raw base64 string"
131
- ],
132
- "parameter_limits": {
133
- "max_new_tokens": "1-2048",
134
- "temperature": "0.01-2.0",
135
- "top_p": "0.01-1.0",
136
- "repetition_penalty": "1.0-2.0"
137
- },
138
- "query_field_alternatives": [
139
- "query", "text", "prompt"
140
- ]
141
- }
142
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "test_requests": {
3
+ "image_url_request": {
4
+ "description": "ECG analizi için image URL ile istek",
5
+ "request": {
6
+ "inputs": {
7
+ "query": "What are the main features and diagnosis in this ECG image? Provide a concise, clinical answer.",
8
+ "image": "https://i.imgur.com/7uuejqO.jpeg"
9
+ },
10
+ "parameters": {
11
+ "max_new_tokens": 512,
12
+ "temperature": 0.2,
13
+ "top_p": 0.9,
14
+ "repetition_penalty": 1.05,
15
+ "stop": ["</s>"],
16
+ "return_full_text": false
17
+ }
18
+ }
19
+ },
20
+
21
+ "base64_request": {
22
+ "description": "ECG analizi için base64 encoded image ile istek",
23
+ "request": {
24
+ "inputs": {
25
+ "query": "What are the main features and diagnosis in this ECG image? Provide a concise, clinical answer.",
26
+ "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
27
+ },
28
+ "parameters": {
29
+ "max_new_tokens": 512,
30
+ "temperature": 0.2,
31
+ "top_p": 0.9,
32
+ "repetition_penalty": 1.05,
33
+ "stop": ["</s>"],
34
+ "return_full_text": false
35
+ }
36
+ }
37
+ },
38
+
39
+ "minimal_url_request": {
40
+ "description": "Minimal parametrelerle image URL isteği",
41
+ "request": {
42
+ "inputs": {
43
+ "query": "Analyze this ECG image.",
44
+ "image": "https://i.imgur.com/7uuejqO.jpeg"
45
+ },
46
+ "parameters": {
47
+ "temperature": 0.2
48
+ }
49
+ }
50
+ },
51
+
52
+ "minimal_base64_request": {
53
+ "description": "Minimal parametrelerle base64 isteği",
54
+ "request": {
55
+ "inputs": {
56
+ "query": "Analyze this ECG image.",
57
+ "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
58
+ },
59
+ "parameters": {
60
+ "temperature": 0.2
61
+ }
62
+ }
63
+ },
64
+
65
+ "text_only_request": {
66
+ "description": "Sadece text ile istek (image olmadan)",
67
+ "request": {
68
+ "inputs": {
69
+ "query": "What are the common causes of atrial fibrillation?"
70
+ },
71
+ "parameters": {
72
+ "max_new_tokens": 256,
73
+ "temperature": 0.3,
74
+ "top_p": 0.9
75
+ }
76
+ }
77
+ },
78
+
79
+ "advanced_parameters_request": {
80
+ "description": "Gelişmiş parametrelerle image URL isteği",
81
+ "request": {
82
+ "inputs": {
83
+ "query": "Provide detailed ECG analysis including rhythm, rate, intervals, and potential abnormalities.",
84
+ "image": "https://i.imgur.com/7uuejqO.jpeg"
85
+ },
86
+ "parameters": {
87
+ "max_new_tokens": 1024,
88
+ "temperature": 0.1,
89
+ "top_p": 0.95,
90
+ "repetition_penalty": 1.1,
91
+ "stop": ["</s>", "\n\n"],
92
+ "return_full_text": false,
93
+ "do_sample": true
94
+ }
95
+ }
96
+ },
97
+
98
+ "deepseek_enabled_request": {
99
+ "description": "DeepSeek Türkçe yorum özelliği aktif - Image URL",
100
+ "request": {
101
+ "inputs": {
102
+ "query": "What are the main features and diagnosis in this ECG image? Provide a concise, clinical answer.",
103
+ "image": "https://i.imgur.com/7uuejqO.jpeg"
104
+ },
105
+ "parameters": {
106
+ "max_new_tokens": 512,
107
+ "temperature": 0.2,
108
+ "top_p": 0.9,
109
+ "repetition_penalty": 1.05,
110
+ "enable_turkish_commentary": true,
111
+ "deepseek_timeout": 30
112
+ }
113
+ }
114
+ },
115
+
116
+ "deepseek_enabled_base64_request": {
117
+ "description": "DeepSeek Türkçe yorum özelliği aktif - Base64",
118
+ "request": {
119
+ "inputs": {
120
+ "query": "What are the main features and diagnosis in this ECG image? Provide a concise, clinical answer.",
121
+ "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k="
122
+ },
123
+ "parameters": {
124
+ "max_new_tokens": 512,
125
+ "temperature": 0.2,
126
+ "enable_turkish_commentary": true,
127
+ "deepseek_timeout": 25
128
+ }
129
+ }
130
+ },
131
+
132
+ "deepseek_disabled_request": {
133
+ "description": "DeepSeek Türkçe yorum özelliği deaktif",
134
+ "request": {
135
+ "inputs": {
136
+ "query": "Analyze this ECG image briefly.",
137
+ "image": "https://i.imgur.com/7uuejqO.jpeg"
138
+ },
139
+ "parameters": {
140
+ "temperature": 0.2,
141
+ "enable_turkish_commentary": false
142
+ }
143
+ }
144
+ }
145
+ },
146
+
147
+ "expected_response_format": {
148
+ "description": "Beklenen response formatı (DeepSeek aktif)",
149
+ "format": [
150
+ {
151
+ "generated_text": "Answer: This ECG image shows a sinus rhythm with a normal heart rate...",
152
+ "model": "PULSE-7B",
153
+ "processing_method": "pipeline",
154
+ "comment_text": "Bu EKG sonucu normal sinüs ritmi göstermektedir. Kalp atış hızı normaldir ve düzenli bir ritim görülmektedir...",
155
+ "commentary_model": "deepseek-chat",
156
+ "commentary_tokens": 85,
157
+ "commentary_status": "success"
158
+ }
159
+ ]
160
+ },
161
+
162
+ "expected_response_format_no_commentary": {
163
+ "description": "Beklenen response formatı (DeepSeek deaktif)",
164
+ "format": [
165
+ {
166
+ "generated_text": "Answer: This ECG image shows a sinus rhythm with a normal heart rate...",
167
+ "model": "PULSE-7B",
168
+ "processing_method": "pipeline"
169
+ }
170
+ ]
171
+ },
172
+
173
+ "error_response_format": {
174
+ "description": "Hata durumunda response formatı",
175
+ "format": [
176
+ {
177
+ "generated_text": "",
178
+ "error": "Error message here...",
179
+ "model": "PULSE-7B",
180
+ "handler": "Ubden® Team Enhanced Handler",
181
+ "success": false
182
+ }
183
+ ]
184
+ },
185
+
186
+ "usage_notes": {
187
+ "supported_image_formats": [
188
+ "JPEG", "PNG", "GIF", "BMP", "WebP"
189
+ ],
190
+ "supported_input_methods": [
191
+ "HTTP/HTTPS URL",
192
+ "Base64 encoded with data URI prefix",
193
+ "Raw base64 string"
194
+ ],
195
+ "parameter_limits": {
196
+ "max_new_tokens": "1-2048",
197
+ "temperature": "0.01-2.0",
198
+ "top_p": "0.01-1.0",
199
+ "repetition_penalty": "1.0-2.0"
200
+ },
201
+ "query_field_alternatives": [
202
+ "query", "text", "prompt"
203
+ ],
204
+ "deepseek_integration": {
205
+ "enable_turkish_commentary": "true/false (default: true)",
206
+ "deepseek_timeout": "10-60 seconds (default: 30)",
207
+ "environment_variable": "deep_key (DeepSeek API key)",
208
+ "commentary_status_values": [
209
+ "success", "failed", "unavailable", "api_key_missing", "no_text"
210
+ ]
211
+ },
212
+ "response_fields_with_commentary": [
213
+ "generated_text", "model", "processing_method",
214
+ "comment_text", "commentary_model", "commentary_tokens", "commentary_status"
215
+ ],
216
+ "response_fields_without_commentary": [
217
+ "generated_text", "model", "processing_method"
218
+ ]
219
+ }
220
+ }
utils.py ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ PULSE-7B Handler Utilities
3
+ Ubden® Team - Performance monitoring and helper functions
4
+ """
5
+
6
+ import time
7
+ import torch
8
+ import psutil
9
+ import logging
10
+ import os
11
+ import json
12
+ import requests
13
+ from typing import Dict, Any, Optional
14
+ from functools import wraps
15
+
16
+ # Configure logging
17
+ logging.basicConfig(level=logging.INFO)
18
+ logger = logging.getLogger(__name__)
19
+
20
+ class PerformanceMonitor:
21
+ """Performance monitoring utilities for PULSE-7B handler"""
22
+
23
+ def __init__(self):
24
+ self.metrics = {
25
+ 'total_requests': 0,
26
+ 'successful_requests': 0,
27
+ 'failed_requests': 0,
28
+ 'image_url_requests': 0,
29
+ 'base64_requests': 0,
30
+ 'text_only_requests': 0,
31
+ 'total_generation_time': 0.0,
32
+ 'total_image_processing_time': 0.0
33
+ }
34
+
35
+ def log_request(self, request_type: str, success: bool,
36
+ generation_time: float = 0.0,
37
+ image_processing_time: float = 0.0):
38
+ """Log request metrics"""
39
+ self.metrics['total_requests'] += 1
40
+
41
+ if success:
42
+ self.metrics['successful_requests'] += 1
43
+ else:
44
+ self.metrics['failed_requests'] += 1
45
+
46
+ if request_type == 'image_url':
47
+ self.metrics['image_url_requests'] += 1
48
+ elif request_type == 'base64':
49
+ self.metrics['base64_requests'] += 1
50
+ else:
51
+ self.metrics['text_only_requests'] += 1
52
+
53
+ self.metrics['total_generation_time'] += generation_time
54
+ self.metrics['total_image_processing_time'] += image_processing_time
55
+
56
+ def get_stats(self) -> Dict[str, Any]:
57
+ """Get current performance statistics"""
58
+ total_requests = self.metrics['total_requests']
59
+ if total_requests == 0:
60
+ return self.metrics
61
+
62
+ success_rate = (self.metrics['successful_requests'] / total_requests) * 100
63
+ avg_generation_time = self.metrics['total_generation_time'] / total_requests
64
+ avg_image_processing_time = self.metrics['total_image_processing_time'] / max(
65
+ self.metrics['image_url_requests'] + self.metrics['base64_requests'], 1
66
+ )
67
+
68
+ return {
69
+ **self.metrics,
70
+ 'success_rate_percent': round(success_rate, 2),
71
+ 'avg_generation_time_seconds': round(avg_generation_time, 3),
72
+ 'avg_image_processing_time_seconds': round(avg_image_processing_time, 3)
73
+ }
74
+
75
+ def reset_stats(self):
76
+ """Reset all metrics"""
77
+ for key in self.metrics:
78
+ self.metrics[key] = 0 if isinstance(self.metrics[key], int) else 0.0
79
+
80
+ def timing_decorator(func):
81
+ """Decorator to measure function execution time"""
82
+ @wraps(func)
83
+ def wrapper(*args, **kwargs):
84
+ start_time = time.time()
85
+ try:
86
+ result = func(*args, **kwargs)
87
+ execution_time = time.time() - start_time
88
+ logger.info(f"{func.__name__} completed in {execution_time:.3f}s")
89
+ return result, execution_time
90
+ except Exception as e:
91
+ execution_time = time.time() - start_time
92
+ logger.error(f"{func.__name__} failed in {execution_time:.3f}s: {e}")
93
+ raise e
94
+ return wrapper
95
+
96
+ def get_system_info() -> Dict[str, Any]:
97
+ """Get current system resource information"""
98
+ cpu_percent = psutil.cpu_percent(interval=1)
99
+ memory = psutil.virtual_memory()
100
+
101
+ system_info = {
102
+ 'cpu_usage_percent': cpu_percent,
103
+ 'memory_total_gb': round(memory.total / (1024**3), 2),
104
+ 'memory_used_gb': round(memory.used / (1024**3), 2),
105
+ 'memory_available_gb': round(memory.available / (1024**3), 2),
106
+ 'memory_usage_percent': memory.percent
107
+ }
108
+
109
+ # Add GPU info if available
110
+ if torch.cuda.is_available():
111
+ gpu_memory = torch.cuda.memory_stats()
112
+ system_info.update({
113
+ 'gpu_available': True,
114
+ 'gpu_memory_allocated_gb': round(
115
+ torch.cuda.memory_allocated() / (1024**3), 2
116
+ ),
117
+ 'gpu_memory_reserved_gb': round(
118
+ torch.cuda.memory_reserved() / (1024**3), 2
119
+ ),
120
+ 'gpu_device_name': torch.cuda.get_device_name(0)
121
+ })
122
+ else:
123
+ system_info['gpu_available'] = False
124
+
125
+ return system_info
126
+
127
+ def validate_image_input(image_input: str) -> Dict[str, Any]:
128
+ """Validate image input and return metadata"""
129
+ if not image_input or not isinstance(image_input, str):
130
+ return {'valid': False, 'type': None, 'error': 'Invalid input type'}
131
+
132
+ # Check if URL
133
+ if image_input.startswith(('http://', 'https://')):
134
+ return {
135
+ 'valid': True,
136
+ 'type': 'url',
137
+ 'length': len(image_input),
138
+ 'domain': image_input.split('/')[2] if '/' in image_input else 'unknown'
139
+ }
140
+
141
+ # Check if base64
142
+ elif image_input.startswith('data:image/') or len(image_input) > 100:
143
+ is_data_url = image_input.startswith('data:')
144
+ base64_data = image_input
145
+
146
+ if is_data_url:
147
+ if 'base64,' in image_input:
148
+ base64_data = image_input.split('base64,')[1]
149
+ else:
150
+ return {'valid': False, 'type': 'base64', 'error': 'Invalid data URL format'}
151
+
152
+ # Estimate decoded size
153
+ estimated_size = len(base64_data) * 3 // 4
154
+
155
+ return {
156
+ 'valid': True,
157
+ 'type': 'base64',
158
+ 'is_data_url': is_data_url,
159
+ 'base64_length': len(base64_data),
160
+ 'estimated_size_bytes': estimated_size,
161
+ 'estimated_size_kb': round(estimated_size / 1024, 2)
162
+ }
163
+
164
+ return {'valid': False, 'type': None, 'error': 'Unrecognized format'}
165
+
166
+ def sanitize_parameters(parameters: Dict[str, Any]) -> Dict[str, Any]:
167
+ """Sanitize and validate generation parameters"""
168
+ sanitized = {}
169
+
170
+ # Max new tokens
171
+ max_new_tokens = parameters.get('max_new_tokens', 512)
172
+ sanitized['max_new_tokens'] = max(1, min(max_new_tokens, 2048))
173
+
174
+ # Temperature
175
+ temperature = parameters.get('temperature', 0.2)
176
+ sanitized['temperature'] = max(0.01, min(temperature, 2.0))
177
+
178
+ # Top-p
179
+ top_p = parameters.get('top_p', 0.9)
180
+ sanitized['top_p'] = max(0.01, min(top_p, 1.0))
181
+
182
+ # Repetition penalty
183
+ repetition_penalty = parameters.get('repetition_penalty', 1.05)
184
+ sanitized['repetition_penalty'] = max(1.0, min(repetition_penalty, 2.0))
185
+
186
+ # Stop sequences
187
+ stop = parameters.get('stop', ['</s>'])
188
+ if isinstance(stop, str):
189
+ stop = [stop]
190
+ sanitized['stop'] = stop[:5] # Limit to 5 stop sequences
191
+
192
+ # Return full text
193
+ sanitized['return_full_text'] = bool(parameters.get('return_full_text', False))
194
+
195
+ # Do sample
196
+ sanitized['do_sample'] = bool(parameters.get('do_sample', sanitized['temperature'] > 0.01))
197
+
198
+ return sanitized
199
+
200
+ def create_health_check() -> Dict[str, Any]:
201
+ """Create a health check response"""
202
+ try:
203
+ system_info = get_system_info()
204
+
205
+ health_status = {
206
+ 'status': 'healthy',
207
+ 'timestamp': time.time(),
208
+ 'system': system_info,
209
+ 'model': 'PULSE-7B',
210
+ 'handler_version': '2.0.0',
211
+ 'features': [
212
+ 'image_url_support',
213
+ 'base64_image_support',
214
+ 'stop_sequences',
215
+ 'parameter_validation',
216
+ 'performance_monitoring'
217
+ ]
218
+ }
219
+
220
+ # Check if system is under stress
221
+ if system_info['memory_usage_percent'] > 90:
222
+ health_status['warnings'] = ['High memory usage']
223
+
224
+ if system_info['cpu_usage_percent'] > 90:
225
+ health_status.setdefault('warnings', []).append('High CPU usage')
226
+
227
+ return health_status
228
+
229
+ except Exception as e:
230
+ return {
231
+ 'status': 'unhealthy',
232
+ 'timestamp': time.time(),
233
+ 'error': str(e)
234
+ }
235
+
236
+ class DeepSeekClient:
237
+ """DeepSeek API client for Turkish commentary"""
238
+
239
+ def __init__(self, api_key: Optional[str] = None):
240
+ self.api_key = api_key or os.getenv('deep_key') or os.getenv('DEEPSEEK_API_KEY')
241
+ self.base_url = "https://api.deepseek.com/v1/chat/completions"
242
+ self.headers = {
243
+ "Content-Type": "application/json",
244
+ "Authorization": f"Bearer {self.api_key}" if self.api_key else ""
245
+ }
246
+
247
+ def is_available(self) -> bool:
248
+ """Check if DeepSeek API is available"""
249
+ return bool(self.api_key)
250
+
251
+ def get_turkish_commentary(self, english_analysis: str, timeout: int = 30) -> Dict[str, Any]:
252
+ """
253
+ Get Turkish commentary for English medical analysis
254
+
255
+ Args:
256
+ english_analysis: English medical analysis text
257
+ timeout: Request timeout in seconds
258
+
259
+ Returns:
260
+ Dict with success status and commentary
261
+ """
262
+ if not self.is_available():
263
+ return {
264
+ "success": False,
265
+ "error": "DeepSeek API key not configured",
266
+ "comment_text": ""
267
+ }
268
+
269
+ try:
270
+ # Prepare the prompt for Turkish medical commentary
271
+ prompt = f"""Bu bir EKG sonucu klinik incelemesi. Aşağıdaki İngilizce medikal analizi Türkçe olarak yorumla ve hasta için anlaşılır bir dilde açıkla:
272
+
273
+ "{english_analysis}"
274
+
275
+ Lütfen:
276
+ 1. Medikal terimleri Türkçe karşılıklarıyla açıkla
277
+ 2. Hastanın anlayabileceği basit bir dille yaz
278
+ 3. Gerekirse aciliyet durumu hakkında bilgi ver
279
+ 4. Kısa ve net ol
280
+
281
+ Türkçe Yorum:"""
282
+
283
+ payload = {
284
+ "model": "deepseek-chat",
285
+ "messages": [
286
+ {
287
+ "role": "system",
288
+ "content": "Sen deneyimli bir kardiyolog doktorsun. EKG sonuçlarını Türkçe olarak hastalar için anlaşılır şekilde açıklıyorsun."
289
+ },
290
+ {
291
+ "role": "user",
292
+ "content": prompt
293
+ }
294
+ ],
295
+ "temperature": 0.3,
296
+ "max_tokens": 500,
297
+ "stream": False
298
+ }
299
+
300
+ logger.info("🔄 DeepSeek API'ye Türkçe yorum için istek gönderiliyor...")
301
+
302
+ response = requests.post(
303
+ self.base_url,
304
+ headers=self.headers,
305
+ json=payload,
306
+ timeout=timeout
307
+ )
308
+
309
+ response.raise_for_status()
310
+ result = response.json()
311
+
312
+ if 'choices' in result and len(result['choices']) > 0:
313
+ comment_text = result['choices'][0]['message']['content'].strip()
314
+
315
+ # Clean up the response - remove "Türkçe Yorum:" prefix if present
316
+ if comment_text.startswith("Türkçe Yorum:"):
317
+ comment_text = comment_text[13:].strip()
318
+
319
+ logger.info("✅ DeepSeek'ten Türkçe yorum başarıyla alındı")
320
+
321
+ return {
322
+ "success": True,
323
+ "comment_text": comment_text,
324
+ "model": "deepseek-chat",
325
+ "tokens_used": result.get('usage', {}).get('total_tokens', 0)
326
+ }
327
+ else:
328
+ return {
329
+ "success": False,
330
+ "error": "DeepSeek API'den geçersiz yanıt",
331
+ "comment_text": ""
332
+ }
333
+
334
+ except requests.exceptions.Timeout:
335
+ logger.error("❌ DeepSeek API timeout")
336
+ return {
337
+ "success": False,
338
+ "error": "DeepSeek API timeout - istek zaman aşımına uğradı",
339
+ "comment_text": ""
340
+ }
341
+
342
+ except requests.exceptions.RequestException as e:
343
+ logger.error(f"❌ DeepSeek API request error: {e}")
344
+ return {
345
+ "success": False,
346
+ "error": f"DeepSeek API bağlantı hatası: {str(e)}",
347
+ "comment_text": ""
348
+ }
349
+
350
+ except Exception as e:
351
+ logger.error(f"❌ DeepSeek API unexpected error: {e}")
352
+ return {
353
+ "success": False,
354
+ "error": f"DeepSeek API beklenmeyen hata: {str(e)}",
355
+ "comment_text": ""
356
+ }
357
+
358
+ # Global instances
359
+ performance_monitor = PerformanceMonitor()
360
+ deepseek_client = DeepSeekClient()