import os
import time
import json
import sys
import gradio as gr
# এই ভার্সনটা ইচ্ছাকৃতভাবে CPU-only — এই Space-এ GPU (ZeroGPU) সাথে llama.cpp-এর
# persistent CUDA context ঠিকভাবে মিলছিল না (প্রতিটা রিকোয়েস্টে GPU attach/detach হওয়ায়
# মডেল আবার লোড করতে হতো), তাই এখানে GPU-চেষ্টা ও `spaces` ডিপেন্ডেন্সি বাদ দেওয়া হয়েছে।
# গুরুত্বপূর্ণ: os.cpu_count() কন্টেইনারের হোস্ট মেশিনের মোট কোর সংখ্যা রিপোর্ট করে,
# কিন্তু Spaces-এর মতো শেয়ার্ড হোস্টে আপনার কন্টেইনার আসলে তার চেয়ে অনেক কম CPU quota
# (cgroup affinity) পায়। os.cpu_count() ধরে থ্রেড বানালে বরাদ্দের চেয়ে বেশি থ্রেড
# একে অপরের সাথে প্রতিযোগিতা করে (context-switch overhead), যেটা গতি কমিয়ে দেয়।
# sched_getaffinity আসল বরাদ্দকৃত কোর সংখ্যা দেয় (শুধু Linux-এ কাজ করে, তাই fallback রাখা হলো)।
try:
cpu_count = len(os.sched_getaffinity(0))
except AttributeError:
cpu_count = os.cpu_count() or 4
print(f"🧵 ব্যবহারযোগ্য CPU কোর সনাক্ত হয়েছে: {cpu_count}")
# BLAS/OpenMP ব্যাকএন্ড যদি থাকে, সেটাও যেন একই সংখ্যক থ্রেড ব্যবহার করে —
# নাহলে llama.cpp-এর থ্রেড + BLAS-এর নিজস্ব থ্রেড মিলে ওভারসাবস্ক্রাইব হতে পারে।
# native library লোড হওয়ার আগেই সেট করা জরুরি, তাই llama_cpp ইম্পোর্টের আগে বসানো হলো।
os.environ.setdefault("OMP_NUM_THREADS", str(cpu_count))
os.environ.setdefault("OPENBLAS_NUM_THREADS", str(cpu_count))
# llama_cpp ইম্পোর্ট
#
# নোট: Bonsai-27B-Q1_0.gguf-এর Q1_0 (1-বিট) ফরম্যাট mainline llama.cpp-তেই সাপোর্টেড —
# CPU ব্যাকএন্ডে কাজ করার জন্য আলাদা কোনো কাস্টম কার্নেল/ফর্ক লাগে না।
try:
from llama_cpp import Llama
print("✅ llama-cpp-python ইম্পোর্ট সফল")
except Exception as e:
print(f"❌ llama-cpp-python ইম্পোর্ট ব্যর্থ: {e}")
sys.exit(1)
# --- ১. মডেল কনফিগারেশন ---
#MODEL_REPO = "prism-ml/Bonsai-27B-gguf"
#MODEL_FILE = "Bonsai-27B-Q1_0.gguf"
# ✅ বর্তমানে সক্রিয়: Bonsai 8B (~1.16 GB)
MODEL_REPO = "prism-ml/Bonsai-8B-gguf"
MODEL_FILE = "Bonsai-8B-Q1_0.gguf"
print("⏳ মডেল ডাউনলোড হচ্ছে...")
from huggingface_hub import hf_hub_download
model_path = hf_hub_download(
repo_id=MODEL_REPO,
filename=MODEL_FILE,
local_dir="./models",
token=None
)
print(f"✅ ডাউনলোড সম্পূর্ণ: {model_path}")
# --- ২. মডেল লোড ---
print("⏳ মডেল লোড হচ্ছে...")
def load_model():
"""CPU-তে সর্বোচ্চ থ্রুপুটের জন্য টিউন করা লোড"""
print("🐢 CPU মোডে লোড হচ্ছে...")
llm = Llama(
model_path=model_path,
n_ctx=4096, # কনটেক্সট যত বড়, প্রম্পট প্রসেসিং তত ধীর — CPU-তে রক্ষণশীল রাখা ভালো
n_gpu_layers=0, # CPU-only
n_threads=cpu_count, # জেনারেশন থ্রেড — আসল বরাদ্দকৃত কোর সংখ্যা
n_threads_batch=cpu_count, # প্রম্পট-প্রসেসিং (ব্যাচ) থ্রেড
n_batch=256, # CPU-তে ছোট batch সাধারণত বড়টার চেয়ে কার্যকর হয়
type_k=8, # KV cache-এর K অংশ Q8_0-এ কোয়ান্টাইজ — মেমরি-ব্যান্ডউইথ বাঁচায়
# (V cache কোয়ান্টাইজেশনের জন্য flash_attn লাগে, তাই এটা বাদ)
use_mmap=True, # ডিস্ক থেকে সরাসরি ম্যাপ করে, RAM-এ পুরো কপি লাগে না
use_mlock=False, # RAM লক না করা — কম RAM এর হোস্টে সোয়াপ/ক্র্যাশ ঠেকায়
verbose=True
)
print("✅ CPU মোডে সফলভাবে লোড হয়েছে!")
return llm, "CPU"
try:
llm, device_mode = load_model()
except Exception as e:
print(f"❌ মডেল লোড করা সম্ভব হয়নি: {e}")
sys.exit(1)
# --- ৩. ওয়ার্মআপ ---
try:
print("🔥 ওয়ার্মআপ হচ্ছে...")
list(llm("<|im_start|>user\nহাই<|im_end|>\n<|im_start|>assistant\n