Upload 14 files
Browse filesmajor update with deepseek integration
- deployment_guide.md +133 -0
- generation_config.json +43 -2
- handler.py +162 -13
- requirements.txt +17 -1
- test_requests.json +220 -142
- 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 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
| 259 |
"generated_text": generated_text,
|
| 260 |
"model": "PULSE-7B",
|
| 261 |
"processing_method": "pipeline"
|
| 262 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
else:
|
| 264 |
-
|
|
|
|
| 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 |
-
|
|
|
|
| 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 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
"
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
"
|
| 133 |
-
"
|
| 134 |
-
"
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 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()
|