zenkamind-api / app.py
Zenkad's picture
Update app.py
90e814c verified
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse, HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
import json
import os
import socket
from datetime import datetime, date
import requests
import logging
import threading
# Logging ayarı
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = FastAPI(
title="AI Chat API",
version="2.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
# TÜM CORS izinleri - Telefondan erişim için
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Tüm domain'lere izin ver
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"]
)
# Dosya yolları
DB_PATH = "users.json"
HF_TOKEN = os.getenv("HF_TOKEN", "")
MODEL_NAME = "mistralai/Mistral-7B-Instruct-v0.2"
API_URL = f"https://api-inference.huggingface.co/models/{MODEL_NAME}"
# Limitler
DAILY_LIMIT = 100
MAX_TOKENS = 150
def get_local_ip():
"""Bilgisayarın IP adresini bul"""
try:
# İnternet bağlantısı olan bir socket oluştur
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80)) # Google DNS
ip = s.getsockname()[0]
s.close()
return ip
except:
return "127.0.0.1"
# Sunucu bilgileri
SERVER_IP = get_local_ip()
SERVER_PORT = 8000
BASE_URL = f"http://{SERVER_IP}:{SERVER_PORT}"
# QR kodu oluşturmak için
def generate_qr_code_url():
"""Telefonda açmak için QR kodu URL'si"""
import urllib.parse
html_url = f"{BASE_URL}/test"
qr_url = f"https://api.qrserver.com/v1/create-qr-code/?size=200x200&data={urllib.parse.quote(html_url)}"
return qr_url
def load_db():
"""Kullanıcı veritabanını yükle"""
try:
if os.path.exists(DB_PATH):
with open(DB_PATH, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
logger.error(f"Veritabanı yükleme hatası: {e}")
return {}
return {}
def save_db(data):
"""Kullanıcı veritabanını kaydet"""
try:
with open(DB_PATH, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
return True
except Exception as e:
logger.error(f"Veritabanı kaydetme hatası: {e}")
return False
def get_model_response_simple(message):
"""Basit model yanıtı - Hata toleranslı"""
if not HF_TOKEN:
return "🔑 API token bulunamadı. Lütfen HF_TOKEN ayarlayın."
try:
# Daha kısa ve basit prompt
prompt = f"Kullanıcı soruyor: {message}\nYanıt:"
response = requests.post(
API_URL,
headers={
"Authorization": f"Bearer {HF_TOKEN}",
"Content-Type": "application/json"
},
json={
"inputs": prompt,
"parameters": {
"max_new_tokens": MAX_TOKENS,
"temperature": 0.7,
"top_p": 0.9,
"do_sample": True,
"return_full_text": False
}
},
timeout=15
)
logger.info(f"API Status: {response.status_code}")
if response.status_code == 200:
result = response.json()
# Farklı formatları işle
if isinstance(result, list):
for item in result:
if isinstance(item, dict):
# Format: [{'generated_text': '...'}]
if 'generated_text' in item:
text = item['generated_text']
if isinstance(text, str):
# Prompt'u temizle
text = text.replace(prompt, "").strip()
if text:
return text
# Diğer formatlar
for key, value in item.items():
if isinstance(value, str) and len(value) > 10:
cleaned = value.replace(prompt, "").strip()
if cleaned:
return cleaned
return "🤖 Yanıt alındı ancak işlenemedi."
elif response.status_code == 503:
return "⏳ Model yükleniyor, lütfen 15 saniye bekleyin..."
elif response.status_code == 429:
return "🚫 Çok fazla istek, lütfen bekleyin."
else:
return f"❌ API hatası: {response.status_code}"
except requests.exceptions.Timeout:
return "⏱️ Zaman aşımı, lütfen daha kısa soru sorun."
except Exception as e:
logger.error(f"Model hatası: {e}")
return f"⚠️ Teknik hata: {str(e)[:50]}"
@app.get("/", response_class=HTMLResponse)
async def home():
"""Ana sayfa - QR kodu ve bağlantılar"""
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Chat API - Telefon Erişimi</title>
<style>
body {{
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: white;
}}
.container {{
background: rgba(255,255,255,0.95);
border-radius: 20px;
padding: 30px;
color: #333;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
}}
h1 {{ color: #4f46e5; }}
.qr-container {{
text-align: center;
margin: 30px 0;
padding: 20px;
background: #f8fafc;
border-radius: 15px;
}}
.qr-code {{
width: 200px;
height: 200px;
margin: 0 auto;
background: white;
padding: 10px;
border-radius: 10px;
}}
.link-box {{
background: #e0f2fe;
padding: 15px;
border-radius: 10px;
margin: 10px 0;
word-break: break-all;
}}
.btn {{
display: inline-block;
background: #4f46e5;
color: white;
padding: 12px 24px;
text-decoration: none;
border-radius: 10px;
margin: 5px;
font-weight: bold;
}}
.status {{
padding: 10px;
border-radius: 8px;
margin: 10px 0;
}}
.success {{ background: #d1fae5; color: #065f46; }}
.warning {{ background: #fef3c7; color: #92400e; }}
.error {{ background: #fee2e2; color: #dc2626; }}
</style>
</head>
<body>
<div class="container">
<h1>🤖 AI Chat API</h1>
<p>Telefonunuzdan bağlanmak için:</p>
<div class="status {'success' if HF_TOKEN else 'warning'}">
<strong>Model Durumu:</strong> {'✅ Hazır' if HF_TOKEN else '⚠️ Token Eksik'}
</div>
<div class="qr-container">
<h3>📱 Telefon ile Bağlan</h3>
<p>Telefon kameranızla bu QR kodu tarayın:</p>
<div class="qr-code">
<img src="{generate_qr_code_url()}" width="200" height="200">
</div>
<p style="font-size: 14px; color: #666;">veya bağlantıya tıklayın</p>
</div>
<h3>🔗 Bağlantılar:</h3>
<div class="link-box">
<strong>Test Arayüzü:</strong><br>
<a href="/test" class="btn">📲 Aç</a>
<a href="{BASE_URL}/test" target="_blank">{BASE_URL}/test</a>
</div>
<div class="link-box">
<strong>API Dokümantasyon:</strong><br>
<a href="/docs" class="btn">📚 Aç</a>
<a href="{BASE_URL}/docs" target="_blank">{BASE_URL}/docs</a>
</div>
<div class="link-box">
<strong>Health Check:</strong><br>
<a href="/health" class="btn">🩺 Aç</a>
<a href="{BASE_URL}/health" target="_blank">{BASE_URL}/health</a>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 2px solid #e5e7eb;">
<h4>📋 Bilgiler:</h4>
<p><strong>IP Adresi:</strong> {SERVER_IP}</p>
<p><strong>Port:</strong> {SERVER_PORT}</p>
<p><strong>Model:</strong> {MODEL_NAME}</p>
<p><strong>Günlük Limit:</strong> {DAILY_LIMIT} sorgu/kullanıcı</p>
</div>
</div>
</body>
</html>
"""
return HTMLResponse(content=html_content)
@app.get("/health")
async def health():
"""Sağlık kontrolü - JSON formatında"""
db_status = "exists" if os.path.exists(DB_PATH) else "missing"
token_exists = bool(HF_TOKEN)
return {
"status": "healthy" if token_exists else "no_token",
"timestamp": datetime.now().isoformat(),
"server_ip": SERVER_IP,
"port": SERVER_PORT,
"model": MODEL_NAME,
"token": "valid" if token_exists else "missing",
"database": db_status,
"daily_limit": DAILY_LIMIT,
"api_url": f"http://{SERVER_IP}:{SERVER_PORT}",
"test_url": f"http://{SERVER_IP}:{SERVER_PORT}/test"
}
@app.get("/test", response_class=HTMLResponse)
async def test_page():
"""Test arayüzü sayfası"""
with open("test.html", "r", encoding="utf-8") as f:
html_content = f.read()
# API URL'sini dinamik olarak ayarla
html_content = html_content.replace(
'const API_BASE_URL = \'http://localhost:8000\';',
f'const API_BASE_URL = \'http://{SERVER_IP}:{SERVER_PORT}\';'
)
return HTMLResponse(content=html_content)
@app.post("/api/chat")
async def chat(request: Request):
"""Chat endpoint'i"""
try:
data = await request.json()
email = data.get("email", "").strip().lower()
message = data.get("message", "").strip()
logger.info(f"Chat isteği: {email} - {message[:30]}...")
# Validasyon
if not email or "@" not in email:
raise HTTPException(400, "Geçerli email gerekli")
if not message:
raise HTTPException(400, "Mesaj boş olamaz")
if len(message) > 500:
raise HTTPException(400, "Mesaj çok uzun (max 500 karakter)")
# Veritabanı
db = load_db()
today = date.today().isoformat()
if email not in db:
db[email] = {
"count": 0,
"total": 0,
"last_reset": today,
"created": datetime.now().isoformat()
}
user = db[email]
# Limit kontrol
if user["last_reset"] != today:
user["count"] = 0
user["last_reset"] = today
if user["count"] >= DAILY_LIMIT:
raise HTTPException(429, f"Günlük limit ({DAILY_LIMIT}) doldu")
# Model yanıtı
ai_response = get_model_response_simple(message)
user["count"] += 1
user["total"] += 1
save_db(db)
return {
"response": ai_response,
"status": "success",
"remaining": DAILY_LIMIT - user["count"],
"used_today": user["count"],
"total_used": user["total"],
"timestamp": datetime.now().isoformat()
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Chat hatası: {e}")
raise HTTPException(500, f"Sunucu hatası: {str(e)}")
@app.get("/api/stats/{email}")
async def get_stats(email: str):
"""İstatistikler"""
email = email.strip().lower()
db = load_db()
if email not in db:
raise HTTPException(404, "Kullanıcı bulunamadı")
user = db[email]
today = date.today().isoformat()
if user["last_reset"] != today:
user["count"] = 0
user["last_reset"] = today
save_db(db)
return {
"email": email,
"used_today": user["count"],
"remaining_today": DAILY_LIMIT - user["count"],
"total_used": user["total"],
"last_reset": user["last_reset"],
"created": user.get("created", "unknown")
}
@app.get("/api/reset/{email}")
async def reset_limit(email: str):
"""Limit sıfırlama (test için)"""
email = email.strip().lower()
db = load_db()
if email in db:
db[email]["count"] = 0
db[email]["last_reset"] = date.today().isoformat()
save_db(db)
return {"status": "success", "message": f"{email} limiti sıfırlandı"}
else:
raise HTTPException(404, "Kullanıcı bulunamadı")
@app.get("/api/info")
async def api_info():
"""API bilgileri"""
return {
"api_name": "AI Chat API",
"version": "2.0.0",
"model": MODEL_NAME,
"server_ip": SERVER_IP,
"port": SERVER_PORT,
"base_url": f"http://{SERVER_IP}:{SERVER_PORT}",
"endpoints": {
"home": "/",
"test_ui": "/test",
"chat": "POST /api/chat",
"health": "GET /health",
"stats": "GET /api/stats/{email}",
"reset": "GET /api/reset/{email}",
"docs": "/docs"
}
}
if __name__ == "__main__":
import uvicorn
logger.info("=" * 60)
logger.info("🤖 AI Chat API Başlatılıyor...")
logger.info(f"📡 Sunucu IP: {SERVER_IP}")
logger.info(f"🔌 Port: {SERVER_PORT}")
logger.info(f"🧠 Model: {MODEL_NAME}")
logger.info(f"🔑 Token: {'✅ Var' if HF_TOKEN else '❌ Yok'}")
logger.info(f"📊 Günlük Limit: {DAILY_LIMIT}")
logger.info("=" * 60)
logger.info("📱 Telefon bağlantıları:")
logger.info(f" • Test Arayüzü: http://{SERVER_IP}:{SERVER_PORT}/test")
logger.info(f" • API Doküman: http://{SERVER_IP}:{SERVER_PORT}/docs")
logger.info(f" • Health Check: http://{SERVER_IP}:{SERVER_PORT}/health")
logger.info("=" * 60)
# Uvicorn'u başlat
uvicorn.run(
app,
host="0.0.0.0", # Tüm ağlardan erişime izin ver
port=SERVER_PORT,
reload=True,
log_level="info",
access_log=True
)