Update app.py
Browse files
app.py
CHANGED
@@ -7,6 +7,10 @@ import tiktoken
|
|
7 |
from datetime import datetime, timedelta
|
8 |
from collections import defaultdict, deque
|
9 |
from detoxify import Detoxify
|
|
|
|
|
|
|
|
|
10 |
|
11 |
app = Flask(__name__, static_folder='static', template_folder='templates')
|
12 |
|
@@ -17,9 +21,7 @@ print("Model loaded successfully.")
|
|
17 |
API_KEY = os.getenv('API_KEY', 'your-api-key-here')
|
18 |
|
19 |
# --- Geliştirilmiş Metrik Takip Sistemi ---
|
20 |
-
# Son 100 isteğin süresini tutarak daha dinamik bir ortalama elde ederiz
|
21 |
request_durations = deque(maxlen=100)
|
22 |
-
# Son 10 dakika içindeki isteklerin zaman damgalarını tutarak RPM hesaplarız
|
23 |
request_timestamps = deque(maxlen=1000)
|
24 |
|
25 |
daily_requests = defaultdict(int)
|
@@ -40,20 +42,24 @@ def transform_predictions(prediction_dict):
|
|
40 |
|
41 |
scores = {}
|
42 |
for key in category_keys:
|
43 |
-
# Değerleri float'a çevirerek JSON uyumluluğunu garantiliyoruz
|
44 |
scores[key] = float(prediction_dict.get(key, 0.0))
|
45 |
|
46 |
threshold = 0.5
|
47 |
bool_categories = {key: (scores[key] > threshold) for key in category_keys}
|
48 |
-
cat_applied_input_types = {key: (["text"] if scores[key] > 0 else []) for key in category_keys}
|
49 |
flagged = any(bool_categories.values())
|
50 |
|
51 |
-
return flagged, bool_categories, scores
|
52 |
|
53 |
def track_request_metrics(start_time, tokens_count):
|
54 |
end_time = time.time()
|
55 |
duration = end_time - start_time
|
56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
request_durations.append(duration)
|
58 |
request_timestamps.append(datetime.now())
|
59 |
|
@@ -66,7 +72,6 @@ def get_performance_metrics():
|
|
66 |
with concurrent_requests_lock:
|
67 |
current_concurrent = concurrent_requests
|
68 |
|
69 |
-
# Ortalama ve Zirve Yanıt Süresi (Son 100 istek üzerinden)
|
70 |
if not request_durations:
|
71 |
avg_request_time = 0
|
72 |
peak_request_time = 0
|
@@ -74,7 +79,6 @@ def get_performance_metrics():
|
|
74 |
avg_request_time = sum(request_durations) / len(request_durations)
|
75 |
peak_request_time = max(request_durations)
|
76 |
|
77 |
-
# RPM (Requests Per Minute) - Dakikadaki İstek Sayısı
|
78 |
now = datetime.now()
|
79 |
one_minute_ago = now - timedelta(seconds=60)
|
80 |
requests_last_minute = sum(1 for ts in request_timestamps if ts > one_minute_ago)
|
@@ -116,46 +120,52 @@ def moderations():
|
|
116 |
start_time = time.time()
|
117 |
total_tokens = 0
|
118 |
|
|
|
119 |
try:
|
120 |
auth_header = request.headers.get('Authorization')
|
121 |
if not auth_header or not auth_header.startswith("Bearer "):
|
122 |
-
|
|
|
123 |
|
124 |
provided_api_key = auth_header.split(" ")[1]
|
125 |
if provided_api_key != API_KEY:
|
126 |
-
|
|
|
127 |
|
128 |
data = request.get_json()
|
129 |
raw_input = data.get('input')
|
130 |
|
131 |
if raw_input is None:
|
132 |
-
|
|
|
133 |
|
134 |
if isinstance(raw_input, str):
|
135 |
texts = [raw_input]
|
136 |
elif isinstance(raw_input, list):
|
137 |
texts = raw_input
|
138 |
else:
|
139 |
-
|
|
|
140 |
|
141 |
if not texts:
|
142 |
-
|
|
|
143 |
|
144 |
if len(texts) > 10:
|
145 |
-
|
|
|
146 |
|
147 |
for text in texts:
|
148 |
if not isinstance(text, str) or len(text.encode('utf-8')) > 300000:
|
149 |
-
|
|
|
150 |
total_tokens += count_tokens(text)
|
151 |
|
152 |
-
results = []
|
153 |
-
# Detoxify'a tüm listeyi tek seferde vermek performansı artırır
|
154 |
predictions = detoxify_model.predict(texts)
|
155 |
-
|
156 |
for i in range(len(texts)):
|
157 |
single_prediction = {key: value[i] for key, value in predictions.items()}
|
158 |
-
flagged, bool_categories, scores
|
159 |
|
160 |
results.append({
|
161 |
"flagged": flagged,
|
@@ -163,20 +173,25 @@ def moderations():
|
|
163 |
"category_scores": scores,
|
164 |
})
|
165 |
|
166 |
-
track_request_metrics(start_time, total_tokens)
|
167 |
-
|
168 |
response_data = {
|
169 |
"id": "modr-" + uuid.uuid4().hex[:24],
|
170 |
"model": "text-moderation-detoxify-multilingual",
|
171 |
"results": results
|
172 |
}
|
173 |
|
174 |
-
|
|
|
175 |
|
176 |
except Exception as e:
|
177 |
-
|
178 |
-
|
|
|
179 |
finally:
|
|
|
|
|
|
|
|
|
|
|
180 |
with concurrent_requests_lock:
|
181 |
concurrent_requests -= 1
|
182 |
|
@@ -193,6 +208,7 @@ def metrics():
|
|
193 |
return jsonify(get_performance_metrics())
|
194 |
|
195 |
def create_directories_and_files():
|
|
|
196 |
os.makedirs('templates', exist_ok=True)
|
197 |
os.makedirs('static', exist_ok=True)
|
198 |
|
@@ -384,7 +400,7 @@ def create_directories_and_files():
|
|
384 |
<div class="flex justify-between items-center mb-4">
|
385 |
<h3 class="text-lg font-semibold">Summary</h3>
|
386 |
<div class="text-sm text-gray-500 dark:text-gray-400">
|
387 |
-
<i class="fas fa-
|
388 |
</div>
|
389 |
</div>
|
390 |
|
@@ -405,7 +421,7 @@ def create_directories_and_files():
|
|
405 |
<h3 class="text-lg font-semibold mb-4">Request Body</h3>
|
406 |
<div class="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg mb-6 overflow-x-auto">
|
407 |
<pre class="text-sm"><code>{
|
408 |
-
"input": "Text to moderate"
|
409 |
}</code></pre>
|
410 |
</div>
|
411 |
<h3 class="text-lg font-semibold mb-4">Response</h3>
|
@@ -441,6 +457,7 @@ def create_directories_and_files():
|
|
441 |
</footer>
|
442 |
|
443 |
<script>
|
|
|
444 |
const darkModeToggle = document.getElementById('darkModeToggle');
|
445 |
const html = document.documentElement;
|
446 |
|
@@ -647,7 +664,7 @@ def create_directories_and_files():
|
|
647 |
initActivityChart();
|
648 |
document.getElementById('refreshMetrics').addEventListener('click', fetchMetrics);
|
649 |
fetchMetrics();
|
650 |
-
setInterval(fetchMetrics, 15000);
|
651 |
});
|
652 |
</script>
|
653 |
</body>
|
@@ -656,7 +673,4 @@ def create_directories_and_files():
|
|
656 |
if __name__ == '__main__':
|
657 |
create_directories_and_files()
|
658 |
port = int(os.getenv('PORT', 7860))
|
659 |
-
# debug=True'yu production ortamında False yapın.
|
660 |
-
# Modelin yüklenmesi uzun sürdüğü için `use_reloader=False` eklemek,
|
661 |
-
# geliştirme sırasında her dosya değişikliğinde modeli tekrar yüklemesini engeller.
|
662 |
app.run(host='0.0.0.0', port=port, debug=True, use_reloader=False)
|
|
|
7 |
from datetime import datetime, timedelta
|
8 |
from collections import defaultdict, deque
|
9 |
from detoxify import Detoxify
|
10 |
+
import logging
|
11 |
+
|
12 |
+
# Flask'in varsayılan logger'ını daha iyi kullanmak için yapılandırma
|
13 |
+
logging.basicConfig(level=logging.INFO)
|
14 |
|
15 |
app = Flask(__name__, static_folder='static', template_folder='templates')
|
16 |
|
|
|
21 |
API_KEY = os.getenv('API_KEY', 'your-api-key-here')
|
22 |
|
23 |
# --- Geliştirilmiş Metrik Takip Sistemi ---
|
|
|
24 |
request_durations = deque(maxlen=100)
|
|
|
25 |
request_timestamps = deque(maxlen=1000)
|
26 |
|
27 |
daily_requests = defaultdict(int)
|
|
|
42 |
|
43 |
scores = {}
|
44 |
for key in category_keys:
|
|
|
45 |
scores[key] = float(prediction_dict.get(key, 0.0))
|
46 |
|
47 |
threshold = 0.5
|
48 |
bool_categories = {key: (scores[key] > threshold) for key in category_keys}
|
|
|
49 |
flagged = any(bool_categories.values())
|
50 |
|
51 |
+
return flagged, bool_categories, scores
|
52 |
|
53 |
def track_request_metrics(start_time, tokens_count):
|
54 |
end_time = time.time()
|
55 |
duration = end_time - start_time
|
56 |
|
57 |
+
# --- İSTEK ÜZERİNE GÜNCELLENEN KISIM ---
|
58 |
+
# Sunucu taraflı işlem süresini milisaniye olarak terminale logla.
|
59 |
+
# Bu log, arayüzdeki metriklerle tutarlı olacaktır.
|
60 |
+
app.logger.info(f"Server-side processing for moderation request took {duration * 1000:.2f} ms.")
|
61 |
+
# ------------------------------------------
|
62 |
+
|
63 |
request_durations.append(duration)
|
64 |
request_timestamps.append(datetime.now())
|
65 |
|
|
|
72 |
with concurrent_requests_lock:
|
73 |
current_concurrent = concurrent_requests
|
74 |
|
|
|
75 |
if not request_durations:
|
76 |
avg_request_time = 0
|
77 |
peak_request_time = 0
|
|
|
79 |
avg_request_time = sum(request_durations) / len(request_durations)
|
80 |
peak_request_time = max(request_durations)
|
81 |
|
|
|
82 |
now = datetime.now()
|
83 |
one_minute_ago = now - timedelta(seconds=60)
|
84 |
requests_last_minute = sum(1 for ts in request_timestamps if ts > one_minute_ago)
|
|
|
120 |
start_time = time.time()
|
121 |
total_tokens = 0
|
122 |
|
123 |
+
response = None
|
124 |
try:
|
125 |
auth_header = request.headers.get('Authorization')
|
126 |
if not auth_header or not auth_header.startswith("Bearer "):
|
127 |
+
response = jsonify({"error": "Unauthorized"}), 401
|
128 |
+
return response
|
129 |
|
130 |
provided_api_key = auth_header.split(" ")[1]
|
131 |
if provided_api_key != API_KEY:
|
132 |
+
response = jsonify({"error": "Unauthorized"}), 401
|
133 |
+
return response
|
134 |
|
135 |
data = request.get_json()
|
136 |
raw_input = data.get('input')
|
137 |
|
138 |
if raw_input is None:
|
139 |
+
response = jsonify({"error": "Invalid input, 'input' field is required"}), 400
|
140 |
+
return response
|
141 |
|
142 |
if isinstance(raw_input, str):
|
143 |
texts = [raw_input]
|
144 |
elif isinstance(raw_input, list):
|
145 |
texts = raw_input
|
146 |
else:
|
147 |
+
response = jsonify({"error": "Invalid input format, expected string or list of strings"}), 400
|
148 |
+
return response
|
149 |
|
150 |
if not texts:
|
151 |
+
response = jsonify({"error": "Input list cannot be empty"}), 400
|
152 |
+
return response
|
153 |
|
154 |
if len(texts) > 10:
|
155 |
+
response = jsonify({"error": "Too many input items. Maximum 10 allowed."}), 400
|
156 |
+
return response
|
157 |
|
158 |
for text in texts:
|
159 |
if not isinstance(text, str) or len(text.encode('utf-8')) > 300000:
|
160 |
+
response = jsonify({"error": "Each input item must be a string with a maximum of 300k bytes."}), 400
|
161 |
+
return response
|
162 |
total_tokens += count_tokens(text)
|
163 |
|
|
|
|
|
164 |
predictions = detoxify_model.predict(texts)
|
165 |
+
results = []
|
166 |
for i in range(len(texts)):
|
167 |
single_prediction = {key: value[i] for key, value in predictions.items()}
|
168 |
+
flagged, bool_categories, scores = transform_predictions(single_prediction)
|
169 |
|
170 |
results.append({
|
171 |
"flagged": flagged,
|
|
|
173 |
"category_scores": scores,
|
174 |
})
|
175 |
|
|
|
|
|
176 |
response_data = {
|
177 |
"id": "modr-" + uuid.uuid4().hex[:24],
|
178 |
"model": "text-moderation-detoxify-multilingual",
|
179 |
"results": results
|
180 |
}
|
181 |
|
182 |
+
response = jsonify(response_data)
|
183 |
+
return response
|
184 |
|
185 |
except Exception as e:
|
186 |
+
app.logger.error(f"An error occurred: {e}", exc_info=True)
|
187 |
+
response = jsonify({"error": "An internal server error occurred."}), 500
|
188 |
+
return response
|
189 |
finally:
|
190 |
+
# Bu blok her zaman çalışır, response döndürülmeden hemen önce
|
191 |
+
if response and response.status_code < 400:
|
192 |
+
# Sadece başarılı istekleri metrikler için takip et
|
193 |
+
track_request_metrics(start_time, total_tokens)
|
194 |
+
|
195 |
with concurrent_requests_lock:
|
196 |
concurrent_requests -= 1
|
197 |
|
|
|
208 |
return jsonify(get_performance_metrics())
|
209 |
|
210 |
def create_directories_and_files():
|
211 |
+
# Bu fonksiyon HTML/CSS içeriği değişmediği için aynı kalabilir.
|
212 |
os.makedirs('templates', exist_ok=True)
|
213 |
os.makedirs('static', exist_ok=True)
|
214 |
|
|
|
400 |
<div class="flex justify-between items-center mb-4">
|
401 |
<h3 class="text-lg font-semibold">Summary</h3>
|
402 |
<div class="text-sm text-gray-500 dark:text-gray-400">
|
403 |
+
<i class="fas fa-stopwatch mr-1"></i> Round-trip time: <span id="responseTime">0ms</span>
|
404 |
</div>
|
405 |
</div>
|
406 |
|
|
|
421 |
<h3 class="text-lg font-semibold mb-4">Request Body</h3>
|
422 |
<div class="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg mb-6 overflow-x-auto">
|
423 |
<pre class="text-sm"><code>{
|
424 |
+
"input": "Text to moderate"
|
425 |
}</code></pre>
|
426 |
</div>
|
427 |
<h3 class="text-lg font-semibold mb-4">Response</h3>
|
|
|
457 |
</footer>
|
458 |
|
459 |
<script>
|
460 |
+
// JS kodu değişmediği için aynı kalabilir.
|
461 |
const darkModeToggle = document.getElementById('darkModeToggle');
|
462 |
const html = document.documentElement;
|
463 |
|
|
|
664 |
initActivityChart();
|
665 |
document.getElementById('refreshMetrics').addEventListener('click', fetchMetrics);
|
666 |
fetchMetrics();
|
667 |
+
setInterval(fetchMetrics, 15000);
|
668 |
});
|
669 |
</script>
|
670 |
</body>
|
|
|
673 |
if __name__ == '__main__':
|
674 |
create_directories_and_files()
|
675 |
port = int(os.getenv('PORT', 7860))
|
|
|
|
|
|
|
676 |
app.run(host='0.0.0.0', port=port, debug=True, use_reloader=False)
|