Spaces:
Sleeping
Sleeping
| 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]}" | |
| 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) | |
| 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" | |
| } | |
| 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) | |
| 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)}") | |
| 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") | |
| } | |
| 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ı") | |
| 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 | |
| ) |