# app.py - SFArena by sixfingerdev # Temperature hatası düzeltildi from flask import Flask, render_template_string, request, Response, jsonify import asyncio import threading from queue import Queue, Empty from putergenai import PuterClient import json app = Flask(__name__) app.secret_key = 'sfarena-secret-key-2024' # ═══════════════════════════════════════════════════════════════════════════ # GLOBAL DEĞİŞKENLER VE ASYNC YÖNETİMİ # ═══════════════════════════════════════════════════════════════════════════ client = None conversations = {} _loop = None _loop_thread = None _client_lock = threading.Lock() def _run_event_loop(loop): asyncio.set_event_loop(loop) loop.run_forever() def get_event_loop(): global _loop, _loop_thread if _loop is None or not _loop.is_running(): _loop = asyncio.new_event_loop() _loop_thread = threading.Thread(target=_run_event_loop, args=(_loop,), daemon=True) _loop_thread.start() return _loop def run_async(coro): loop = get_event_loop() future = asyncio.run_coroutine_threadsafe(coro, loop) return future.result(timeout=120) # ═══════════════════════════════════════════════════════════════════════════ # MODEL KONFİGÜRASYONU # ═══════════════════════════════════════════════════════════════════════════ # Temperature DESTEKLEMİYEN modeller (reasoning modelleri) NO_TEMPERATURE_MODELS = { "o1", "o1-mini", "o1-pro", "o3", "o3-mini", "o4-mini", "openrouter:openai/o1", "openrouter:openai/o1-mini", "openrouter:openai/o1-pro", "openrouter:openai/o3", "openrouter:openai/o3-mini", "openrouter:openai/o4-mini", } MODELS = { # ═══ OpenAI GPT-5.x ═══ "gpt-5.2": {"name": "GPT-5.2", "provider": "openai"}, "gpt-5.2-chat": {"name": "GPT-5.2 Chat", "provider": "openai"}, "gpt-5.2-pro": {"name": "GPT-5.2 Pro", "provider": "openai"}, "gpt-5.1": {"name": "GPT-5.1", "provider": "openai"}, "gpt-5.1-chat-latest": {"name": "GPT-5.1 Chat Latest", "provider": "openai"}, "gpt-5.1-codex": {"name": "GPT-5.1 Codex", "provider": "openai"}, "gpt-5.1-codex-max": {"name": "GPT-5.1 Codex Max", "provider": "openai"}, "gpt-5.1-codex-mini": {"name": "GPT-5.1 Codex Mini", "provider": "openai"}, "gpt-5": {"name": "GPT-5", "provider": "openai"}, "gpt-5-mini": {"name": "GPT-5 Mini", "provider": "openai"}, "gpt-5-nano": {"name": "GPT-5 Nano", "provider": "openai"}, "gpt-5-chat-latest": {"name": "GPT-5 Chat Latest", "provider": "openai"}, # ═══ OpenAI GPT-4.x ═══ "gpt-4.1": {"name": "GPT-4.1", "provider": "openai"}, "gpt-4.1-mini": {"name": "GPT-4.1 Mini", "provider": "openai"}, "gpt-4.1-nano": {"name": "GPT-4.1 Nano", "provider": "openai"}, "gpt-4.5-preview": {"name": "GPT-4.5 Preview", "provider": "openai"}, "gpt-4o": {"name": "GPT-4o", "provider": "openai"}, "gpt-4o-mini": {"name": "GPT-4o Mini", "provider": "openai"}, # ═══ OpenAI o-Series (NO TEMPERATURE) ═══ "o1": {"name": "o1", "provider": "openai"}, "o1-mini": {"name": "o1 Mini", "provider": "openai"}, "o1-pro": {"name": "o1 Pro", "provider": "openai"}, "o3": {"name": "o3", "provider": "openai"}, "o3-mini": {"name": "o3 Mini", "provider": "openai"}, "o4-mini": {"name": "o4 Mini", "provider": "openai"}, # ═══ OpenRouter OpenAI ═══ "openrouter:openai/gpt-oss-120b": {"name": "GPT-OSS 120B", "provider": "openai"}, "openrouter:openai/gpt-oss-120b:exacto": {"name": "GPT-OSS 120B Exacto", "provider": "openai"}, "openrouter:openai/gpt-oss-20b": {"name": "GPT-OSS 20B", "provider": "openai"}, "openrouter:openai/gpt-oss-20b:free": {"name": "GPT-OSS 20B Free", "provider": "openai"}, "openrouter:openai/gpt-oss-safeguard-20b": {"name": "GPT-OSS Safeguard 20B", "provider": "openai"}, "openrouter:openai/codex-mini": {"name": "Codex Mini", "provider": "openai"}, "openrouter:openai/gpt-5-codex": {"name": "GPT-5 Codex (OR)", "provider": "openai"}, "openrouter:openai/gpt-5.1-codex": {"name": "GPT-5.1 Codex (OR)", "provider": "openai"}, "openrouter:openai/gpt-5.1-codex-max": {"name": "GPT-5.1 Codex Max (OR)", "provider": "openai"}, "openrouter:openai/gpt-5.1-codex-mini": {"name": "GPT-5.1 Codex Mini (OR)", "provider": "openai"}, # ═══ Anthropic Claude ═══ "claude-sonnet-4-5": {"name": "Claude Sonnet 4.5", "provider": "anthropic"}, "claude-haiku-4-5": {"name": "Claude Haiku 4.5", "provider": "anthropic"}, "claude-opus-4-5": {"name": "Claude Opus 4.5", "provider": "anthropic"}, "claude-sonnet-4": {"name": "Claude Sonnet 4", "provider": "anthropic"}, "claude-opus-4": {"name": "Claude Opus 4", "provider": "anthropic"}, "claude-opus-4-1": {"name": "Claude Opus 4.1", "provider": "anthropic"}, # ═══ Google Gemini ═══ "gemini-3-flash-preview": {"name": "Gemini 3 Flash Preview", "provider": "google"}, "gemini-3-pro-preview": {"name": "Gemini 3 Pro Preview", "provider": "google"}, "gemini-2.5-pro": {"name": "Gemini 2.5 Pro", "provider": "google"}, "gemini-2.5-flash": {"name": "Gemini 2.5 Flash", "provider": "google"}, "gemini-2.5-flash-lite": {"name": "Gemini 2.5 Flash Lite", "provider": "google"}, "gemini-2.0-flash": {"name": "Gemini 2.0 Flash", "provider": "google"}, "gemini-2.0-flash-lite": {"name": "Gemini 2.0 Flash Lite", "provider": "google"}, "gemini-1.5-flash": {"name": "Gemini 1.5 Flash", "provider": "google"}, # ═══ xAI Grok ═══ "x-ai/grok-4.1-fast": {"name": "Grok 4.1 Fast", "provider": "xai"}, "x-ai/grok-2-1212": {"name": "Grok 2 1212", "provider": "xai"}, "x-ai/grok-3": {"name": "Grok 3", "provider": "xai"}, "x-ai/grok-3-beta": {"name": "Grok 3 Beta", "provider": "xai"}, "x-ai/grok-3-mini": {"name": "Grok 3 Mini", "provider": "xai"}, "x-ai/grok-3-mini-beta": {"name": "Grok 3 Mini Beta", "provider": "xai"}, "x-ai/grok-4": {"name": "Grok 4", "provider": "xai"}, "x-ai/grok-4-fast:free": {"name": "Grok 4 Fast Free", "provider": "xai"}, "x-ai/grok-code-fast-1": {"name": "Grok Code Fast 1", "provider": "xai"}, # ═══ DeepSeek ═══ "deepseek/deepseek-v3.2": {"name": "DeepSeek V3.2", "provider": "deepseek"}, "deepseek/deepseek-v3.1": {"name": "DeepSeek V3.1", "provider": "deepseek"}, "deepseek/deepseek-r1": {"name": "DeepSeek R1", "provider": "deepseek"}, "deepseek/deepseek-v3": {"name": "DeepSeek V3", "provider": "deepseek"}, # ═══ Amazon Nova ═══ "amazon/nova-micro-v1": {"name": "Nova Micro V1", "provider": "amazon"}, "amazon/nova-2-lite": {"name": "Nova 2 Lite", "provider": "amazon"}, "amazon/nova-premier": {"name": "Nova Premier", "provider": "amazon"}, "amazon/nova-pro": {"name": "Nova Pro", "provider": "amazon"}, # ═══ MiniMax ═══ "openrouter:minimax/minimax-m2.1": {"name": "MiniMax M2.1", "provider": "minimax"}, "openrouter:minimax/minimax-m2": {"name": "MiniMax M2", "provider": "minimax"}, "openrouter:minimax/minimax-m1": {"name": "MiniMax M1", "provider": "minimax"}, "openrouter:minimax/minimax-01": {"name": "MiniMax 01", "provider": "minimax"}, # ═══ Meta Llama ═══ "openrouter:meta-llama/llama-4-maverick": {"name": "Llama 4 Maverick", "provider": "meta"}, # ═══ Essential AI ═══ "openrouter:essentialai/rnj-1-instruct": {"name": "RNJ-1 Instruct", "provider": "essentialai"}, } PROVIDER_LOGOS = { "openai": "https://cdn.worldvectorlogo.com/logos/openai-2.svg", "anthropic": "https://cdn.worldvectorlogo.com/logos/anthropic-1.svg", "google": "https://www.gstatic.com/lamda/images/gemini_sparkle_v002_d4735304ff6292a690345.svg", "xai": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b7/X_logo.jpg/600px-X_logo.jpg", "deepseek": "https://avatars.githubusercontent.com/u/148330875?s=200&v=4", "amazon": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Amazon_logo.svg/1024px-Amazon_logo.svg.png", "minimax": "https://avatars.githubusercontent.com/u/122397016?s=200&v=4", "meta": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7b/Meta_Platforms_Inc._logo.svg/1024px-Meta_Platforms_Inc._logo.svg.png", "essentialai": "https://avatars.githubusercontent.com/u/167503936?s=200&v=4", } PROVIDER_COLORS = { "openai": {"bg": "#10a37f", "text": "#ffffff"}, "anthropic": {"bg": "#d4a574", "text": "#1a1a1a"}, "google": {"bg": "#4285f4", "text": "#ffffff"}, "xai": {"bg": "#000000", "text": "#ffffff"}, "deepseek": {"bg": "#0066ff", "text": "#ffffff"}, "amazon": {"bg": "#ff9900", "text": "#000000"}, "minimax": {"bg": "#6366f1", "text": "#ffffff"}, "meta": {"bg": "#0668e1", "text": "#ffffff"}, "essentialai": {"bg": "#8b5cf6", "text": "#ffffff"}, } def get_model_options(model_id): """Model için uygun options döndür""" options = { "model": model_id, "stream": True, "max_tokens": 8192 } # Temperature desteklemeyen modeller için ekleme if model_id not in NO_TEMPERATURE_MODELS: options["temperature"] = 0.7 return options # ═══════════════════════════════════════════════════════════════════════════ # HTML TEMPLATE # ═══════════════════════════════════════════════════════════════════════════ HTML_TEMPLATE = r''' SFArena - AI Chat Platform
SF
SFArena by sixfingerdev
Model Seçin
-
Bağlanıyor...

SFArena

50+ yapay zeka modeli. GPT-5, Claude Opus, Gemini Pro ...

Merhaba
Python API
Hikaye
JS Async
Enter gönder • Shift+Enter yeni satır
''' # ═══════════════════════════════════════════════════════════════════════════ # FLASK ROUTES # ═══════════════════════════════════════════════════════════════════════════ @app.route('/') def index(): return render_template_string(HTML_TEMPLATE, models=MODELS, provider_logos=PROVIDER_LOGOS, provider_colors=PROVIDER_COLORS) import os # En üste ekle # ... @app.route('/api/init', methods=['POST']) def init_client(): global client try: data = request.json session_id = data.get('session_id', 'default') if session_id not in conversations: conversations[session_id] = [] with _client_lock: if client is None: client = PuterClient() # Şifreyi ortam değişkeninden al password = os.getenv('PUTER_PASSWORD') if not password: raise Exception("PUTER_PASSWORD ortam değişkeni tanımlı değil!") run_async(client.login("sixfinger", password)) return jsonify({"success": True}) except Exception as e: return jsonify({"success": False, "error": str(e)}) @app.route('/api/chat', methods=['POST']) def chat(): global client, conversations data = request.json session_id = data.get('session_id', 'default') user_message = data.get('message', '') model = data.get('model', 'gpt-4o-mini') if session_id not in conversations: conversations[session_id] = [] conversations[session_id].append({"role": "user", "content": user_message}) def generate(): result_queue = Queue() loop = get_event_loop() async def async_stream(): try: messages = [ {"role": "system", "content": "Sen yardımcı bir asistansın. Türkçe yanıt ver."}, ] + conversations[session_id] # Model bazlı options options = get_model_options(model) stream = await client.ai_chat(messages=messages, options=options) full_response = "" async for text_chunk, model_name in stream: if text_chunk: full_response += text_chunk result_queue.put(('chunk', text_chunk)) conversations[session_id].append({"role": "assistant", "content": full_response}) result_queue.put(('done', None)) except Exception as e: result_queue.put(('error', str(e))) asyncio.run_coroutine_threadsafe(async_stream(), loop) while True: try: msg_type, data = result_queue.get(timeout=1200) if msg_type == 'chunk': yield f"data: {json.dumps({'chunk': data})}\n\n" elif msg_type == 'done': yield f"data: {json.dumps({'done': True})}\n\n" break elif msg_type == 'error': yield f"data: {json.dumps({'error': data})}\n\n" break except Empty: yield f"data: {json.dumps({'error': 'Zaman aşımı'})}\n\n" break return Response(generate(), mimetype='text/event-stream') @app.route('/api/clear', methods=['POST']) def clear_history(): data = request.json session_id = data.get('session_id', 'default') if session_id in conversations: conversations[session_id] = [] return jsonify({"success": True}) # ═══════════════════════════════════════════════════════════════════════════ # BAŞLATMA # ═══════════════════════════════════════════════════════════════════════════ if __name__ == '__main__': get_event_loop() print(""" ╔══════════════════════════════════════════════╗ ║ SFArena - AI Chat Platform ║ ║ by sixfingerdev ║ ║ github.com/sixfingerdev ║ ╠══════════════════════════════════════════════╣ ║ Sunucu: http://localhost:5000 ║ ╚══════════════════════════════════════════════╝ """) app.run(debug=False, host='0.0.0.0', port=5000, threaded=True)