from flask import Flask, request, render_template_string, Response, stream_with_context import os import time import json import subprocess import atexit import requests from huggingface_hub import hf_hub_download app = Flask(__name__) # --- ১. আসল ১-বিট Bonsai চালানোর জন্য PrismML-এর llama.cpp ফর্ক বিল্ড করা --- # stock llama-cpp-python-এ Q1_0-এর দ্রুত কার্নেল নেই, তাই আসল স্পিড পেতে # PrismML-এর নিজস্ব ফর্ক সোর্স থেকে বিল্ড করতে হয়। HF Spaces-এ pip install # দিয়ে এটা সম্ভব না, তাই অ্যাপ স্টার্টআপের সময় নিজে থেকে git clone + cmake # build করা হচ্ছে (একবারই হবে, বিল্ড হয়ে গেলে পরের রিস্টার্টে স্কিপ হবে)। LLAMA_CPP_DIR = os.path.join(os.getcwd(), "llama.cpp") LLAMA_SERVER_BIN = os.path.join(LLAMA_CPP_DIR, "build", "bin", "llama-server") LLAMA_SERVER_PORT = 8081 LLAMA_SERVER_URL = f"http://127.0.0.1:{LLAMA_SERVER_PORT}" MODEL_REPO = "prism-ml/Bonsai-1.7B-gguf" MODEL_FILE = "Bonsai-1.7B-Q1_0.gguf" server_process = None llm_ready = False def build_llama_cpp_fork(): """PrismML-এর llama.cpp ফর্ক ক্লোন ও বিল্ড করে (Q1_0 কার্নেলসহ)।""" if os.path.exists(LLAMA_SERVER_BIN): print("✅ llama-server আগে থেকেই বিল্ড করা আছে, বিল্ড স্কিপ করা হলো") return print("⏳ PrismML-এর llama.cpp ফর্ক ক্লোন করা হচ্ছে...") subprocess.run( ["git", "clone", "--depth", "1", "https://github.com/PrismML-Eng/llama.cpp", LLAMA_CPP_DIR], check=True ) print("⏳ CMake কনফিগার করা হচ্ছে...") subprocess.run(["cmake", "-B", "build"], cwd=LLAMA_CPP_DIR, check=True) cpu_count = os.cpu_count() or 2 print(f"⏳ বিল্ড হচ্ছে ({cpu_count} থ্রেড দিয়ে)... এতে কয়েক মিনিট লাগতে পারে") subprocess.run( ["cmake", "--build", "build", "-j", str(cpu_count), "--target", "llama-server"], cwd=LLAMA_CPP_DIR, check=True ) print("✅ বিল্ড সম্পূর্ণ!") def start_llama_server(model_path): """বিল্ড হওয়া llama-server বাইনারি ব্যাকগ্রাউন্ডে চালু করে।""" global server_process cpu_count = os.cpu_count() or 2 print("⏳ llama-server চালু করা হচ্ছে...") server_process = subprocess.Popen([ LLAMA_SERVER_BIN, "-m", model_path, "--host", "127.0.0.1", "--port", str(LLAMA_SERVER_PORT), "-t", str(cpu_count), "-c", "2048" ]) atexit.register(server_process.terminate) # সার্ভার রেডি হওয়া পর্যন্ত অপেক্ষা (হেলথ চেক) for _ in range(90): try: r = requests.get(f"{LLAMA_SERVER_URL}/health", timeout=2) if r.status_code == 200: print("✅ llama-server প্রস্তুত!") return True except Exception: pass time.sleep(2) print("⚠️ নির্ধারিত সময়ে llama-server রেডি হয়নি") return False try: print("⏳ মডেল ডাউনলোড হচ্ছে...") model_path = hf_hub_download( repo_id=MODEL_REPO, filename=MODEL_FILE, local_dir="./models", token=None ) print(f"✅ মডেল ডাউনলোড সম্পূর্ণ: {model_path}") build_llama_cpp_fork() llm_ready = start_llama_server(model_path) except Exception as e: print(f"⚠️ সেটআপে সমস্যা হয়েছে: {e}") llm_ready = False # --- ২. HTML টেমপ্লেট --- HTML_TEMPLATE = """ Bonsai 8B চ্যাট (Hugging Face Space)

🌳 Bonsai 8B (১-বিট) চ্যাট

Hugging Face Space • CPU Inference • মডেল সাইজ: ~১.১৫ GB (Q1_0, 1-bit)
👋 হ্যালো! আমি Bonsai 8B। আপনি কী জানতে চান?
প্রস্তুত

⚙️ প্যারামিটার সেটিংস

""" # --- ৩. Flask রাউট --- @app.route('/') def index(): return render_template_string(HTML_TEMPLATE) @app.route('/chat', methods=['POST']) def chat(): data = request.get_json() user_prompt = data.get('prompt', '') if not user_prompt: return {"error": "কোনো প্রশ্ন নেই"} if not llm_ready: return {"error": "মডেল/সার্ভার প্রস্তুত না। সার্ভার লগ চেক করুন।"} # --- ফ্রন্টএন্ড সেটিংস প্যানেল থেকে পাঠানো প্যারামিটার --- max_tokens = int(data.get('max_tokens', 64)) temperature = float(data.get('temperature', 0.1)) top_p = float(data.get('top_p', 0.7)) top_k = int(data.get('top_k', 20)) repeat_penalty = float(data.get('repeat_penalty', 1.4)) do_sample = bool(data.get('do_sample', False)) think_mode = bool(data.get('think_mode', False)) use_stream = bool(data.get('stream', True)) use_cache = bool(data.get('use_cache', True)) # এখন সত্যিই llama-server-এর prompt cache নিয়ন্ত্রণ করে # থিংক মোড বন্ধ থাকলে "/no_think" যোগ করা হচ্ছে think_suffix = "" if think_mode else " /no_think" full_prompt = f"<|im_start|>user\n{user_prompt}{think_suffix}<|im_end|>\n<|im_start|>assistant\n" payload = { "prompt": full_prompt, "n_predict": max_tokens, "temperature": temperature if do_sample else 0.0, "top_p": top_p, "top_k": top_k, "repeat_penalty": repeat_penalty, "cache_prompt": use_cache, "stream": use_stream, "stop": ["<|im_end|>", "user:", "User:"] } if use_stream: def generate(): try: with requests.post(f"{LLAMA_SERVER_URL}/completion", json=payload, stream=True, timeout=300) as r: for line in r.iter_lines(): if not line: continue line = line.decode('utf-8') if line.startswith("data: "): try: chunk = json.loads(line[len("data: "):]) except json.JSONDecodeError: continue content = chunk.get("content", "") if content: yield content if chunk.get("stop"): break except Exception as e: print(f"❌ জেনারেশন ত্রুটি: {e}") yield f"\n⚠️ মডেল ত্রুটি: {str(e)}" return Response(stream_with_context(generate()), mimetype='text/plain') else: try: r = requests.post(f"{LLAMA_SERVER_URL}/completion", json=payload, timeout=300) result = r.json() output = result.get("content", "").strip() return {"response": output} except Exception as e: print(f"❌ জেনারেশন ত্রুটি: {e}") return {"error": f"মডেল ত্রুটি: {str(e)}"} # --- ৪. সার্ভার চালানো --- if __name__ == '__main__': app.run(host='0.0.0.0', port=7860)