File size: 4,915 Bytes
52614c9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from pydantic import BaseModel
from huggingface_hub import hf_hub_download
from llama_cpp import Llama
app = FastAPI()
# 1. تحميل النموذج من المستودع المحدد
model_path = hf_hub_download(
repo_id="Arabic250/gemma-4-gguf-export",
filename="gemma-4-e4b.gguf"
)
# 2. تهيئة النموذج للعمل على CPU فقط
llm = Llama(
model_path=model_path,
n_ctx=2048,
n_threads=4, # مناسب لموارد Hugging Face المجانية
n_gpu_layers=0 # تعطيل طبقات معالج الرسوميات (CPU Only)
)
class ChatRequest(BaseModel):
message: str
# 3. واجهة React الأمامية مدمجة كـ HTML
HTML_CONTENT = """
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Gemma 4 Chat</title>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 p-4 font-sans">
<div id="root"></div>
<script type="text/babel">
function App() {
const [messages, setMessages] = React.useState([]);
const [input, setInput] = React.useState("");
const [loading, setLoading] = React.useState(false);
const sendMessage = async () => {
if (!input.trim()) return;
const newMessages = [...messages, { role: 'user', text: input }];
setMessages(newMessages);
setInput("");
setLoading(true);
try {
const res = await fetch("/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: input })
});
const data = await res.json();
setMessages([...newMessages, { role: 'model', text: data.response }]);
} catch (error) {
console.error(error);
}
setLoading(false);
};
return (
<div className="max-w-3xl mx-auto bg-white p-6 rounded-lg shadow-lg mt-10 border-t-4 border-blue-500">
<h1 className="text-2xl font-bold mb-6 text-center text-gray-800">محادثة Gemma 4 (React)</h1>
<div className="h-[28rem] overflow-y-auto mb-4 p-4 border rounded bg-gray-50 flex flex-col gap-3">
{messages.map((msg, idx) => (
<div key={idx} className={`p-3 rounded-lg w-fit max-w-[80%] ${msg.role === 'user' ? 'bg-blue-100 self-start' : 'bg-green-100 self-end'}`}>
<strong className="block mb-1 text-xs text-gray-500">{msg.role === 'user' ? 'أنت' : 'النموذج'}</strong>
<span className="text-gray-800">{msg.text}</span>
</div>
))}
{loading && <div className="text-gray-500 text-sm animate-pulse self-end">النموذج يقوم بالمعالجة الآن...</div>}
</div>
<div className="flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
className="flex-1 p-3 border rounded focus:outline-none focus:ring-2 focus:ring-blue-400"
placeholder="اكتب استفسارك هنا..."
/>
<button onClick={sendMessage} disabled={loading} className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 disabled:opacity-50 transition-colors font-semibold">
إرسال
</button>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>
"""
@app.get("/")
def read_root():
return HTMLResponse(content=HTML_CONTENT)
@app.post("/chat")
def chat(request: ChatRequest):
# بناء سياق Gemma بالشكل الصحيح
prompt = f"<start_of_turn>user\n{request.message}<end_of_turn>\n<start_of_turn>model\n"
response = llm(
prompt,
max_tokens=512,
stop=["<end_of_turn>"]
)
return {"response": response['choices'][0]['text'].strip()} |