from flask import Flask, request, jsonify, render_template
import os
import uuid
import time
import threading
import tiktoken
from datetime import datetime, timedelta
from collections import defaultdict, deque
from detoxify import Detoxify
import logging
# Flask'in varsayılan logger'ını daha iyi kullanmak için yapılandırma
logging.basicConfig(level=logging.INFO)
app = Flask(__name__, static_folder='static', template_folder='templates')
print("Loading Detoxify model... This may take a moment.")
detoxify_model = Detoxify('multilingual')
print("Model loaded successfully.")
# --- Geliştirilmiş Metrik Takip Sistemi ---
request_durations = deque(maxlen=100)
request_timestamps = deque(maxlen=1000)
daily_requests = defaultdict(int)
daily_tokens = defaultdict(int)
concurrent_requests = 0
concurrent_requests_lock = threading.Lock()
encoding = tiktoken.get_encoding("cl100k_base")
def count_tokens(text):
return len(encoding.encode(text))
def transform_predictions(prediction_dict):
category_keys = [
"toxicity", "severe_toxicity", "obscene", "threat",
"insult", "identity_attack", "sexual_explicit"
]
scores = {}
for key in category_keys:
scores[key] = float(prediction_dict.get(key, 0.0))
threshold = 0.5
bool_categories = {key: (scores[key] > threshold) for key in category_keys}
flagged = any(bool_categories.values())
return flagged, bool_categories, scores
def track_request_metrics(start_time, tokens_count):
end_time = time.time()
duration = end_time - start_time
# --- İSTEK ÜZERİNE GÜNCELLENEN KISIM ---
# Sunucu taraflı işlem süresini milisaniye olarak terminale logla.
# Bu log, arayüzdeki metriklerle tutarlı olacaktır.
app.logger.info(f"Server-side processing for moderation request took {duration * 1000:.2f} ms.")
# ------------------------------------------
request_durations.append(duration)
request_timestamps.append(datetime.now())
today = datetime.now().strftime("%Y-%m-%d")
daily_requests[today] += 1
daily_tokens[today] += tokens_count
def get_performance_metrics():
global concurrent_requests
with concurrent_requests_lock:
current_concurrent = concurrent_requests
if not request_durations:
avg_request_time = 0
peak_request_time = 0
else:
avg_request_time = sum(request_durations) / len(request_durations)
peak_request_time = max(request_durations)
now = datetime.now()
one_minute_ago = now - timedelta(seconds=60)
requests_last_minute = sum(1 for ts in request_timestamps if ts > one_minute_ago)
today = now.strftime("%Y-%m-%d")
today_requests = daily_requests.get(today, 0)
today_tokens = daily_tokens.get(today, 0)
last_7_days = []
for i in range(7):
date = (now - timedelta(days=i)).strftime("%Y-%m-%d")
last_7_days.append({
"date": date,
"requests": daily_requests.get(date, 0),
"tokens": daily_tokens.get(date, 0)
})
return {
"avg_request_time_ms": avg_request_time * 1000,
"peak_request_time_ms": peak_request_time * 1000,
"requests_per_minute": requests_last_minute,
"concurrent_requests": current_concurrent,
"today_requests": today_requests,
"today_tokens": today_tokens,
"last_7_days": last_7_days
}
@app.route('/')
def home():
return render_template('index.html')
@app.route('/v1/moderations', methods=['POST'])
def moderations():
global concurrent_requests
with concurrent_requests_lock:
concurrent_requests += 1
start_time = time.time()
total_tokens = 0
response = None
try:
data = request.get_json()
raw_input = data.get('input')
if raw_input is None:
response = jsonify({"error": "Invalid input, 'input' field is required"}), 400
return response
if isinstance(raw_input, str):
texts = [raw_input]
elif isinstance(raw_input, list):
texts = raw_input
else:
response = jsonify({"error": "Invalid input format, expected string or list of strings"}), 400
return response
if not texts:
response = jsonify({"error": "Input list cannot be empty"}), 400
return response
if len(texts) > 10:
response = jsonify({"error": "Too many input items. Maximum 10 allowed."}), 400
return response
for text in texts:
if not isinstance(text, str) or len(text.encode('utf-8')) > 300000:
response = jsonify({"error": "Each input item must be a string with a maximum of 300k bytes."}), 400
return response
total_tokens += count_tokens(text)
predictions = detoxify_model.predict(texts)
results = []
for i in range(len(texts)):
single_prediction = {key: value[i] for key, value in predictions.items()}
flagged, bool_categories, scores = transform_predictions(single_prediction)
results.append({
"flagged": flagged,
"categories": bool_categories,
"category_scores": scores,
})
response_data = {
"id": "modr-" + uuid.uuid4().hex[:24],
"model": "text-moderation-detoxify-multilingual",
"results": results
}
response = jsonify(response_data)
return response
except Exception as e:
app.logger.error(f"An error occurred: {e}", exc_info=True)
response = jsonify({"error": "An internal server error occurred."}), 500
return response
finally:
# Bu blok her zaman çalışır, response döndürülmeden hemen önce
if response and response.status_code < 400:
# Sadece başarılı istekleri metrikler için takip et
track_request_metrics(start_time, total_tokens)
with concurrent_requests_lock:
concurrent_requests -= 1
@app.route('/v1/metrics', methods=['GET'])
def metrics():
return jsonify(get_performance_metrics())
def create_directories_and_files():
# Bu fonksiyon HTML/CSS içeriği değişmediği için aynı kalabilir.
os.makedirs('templates', exist_ok=True)
os.makedirs('static', exist_ok=True)
index_path = os.path.join('templates', 'index.html')
if not os.path.exists(index_path):
with open(index_path, 'w', encoding='utf-8') as f:
f.write('''
Text Moderation API
Performance Metrics
Avg. Response (last 100)
0ms
Peak Response (last 100)
0ms
Analysis Results
Summary
Round-trip time: 0ms
API Documentation
Endpoint
POST /v1/moderations
Request Body
{
"input": "Text to moderate"
}
Response
{
"id": "modr-1234567890abcdef",
"model": "text-moderation-detoxify-multilingual",
"results": [
{
"flagged": true,
"categories": {
"toxicity": true,
"severe_toxicity": false,
/* ... other categories */
},
"category_scores": {
"toxicity": 0.95,
"severe_toxicity": 0.1,
/* ... other scores */
}
}
]
}
''')
if __name__ == '__main__':
create_directories_and_files()
port = int(os.getenv('PORT', 7860))
app.run(host='0.0.0.0', port=port, debug=True, use_reloader=False)