diff --git a/SovereignAI-Starter/README.md b/SovereignAI-Starter/README.md index 1291091..03cb6ac 100644 --- a/SovereignAI-Starter/README.md +++ b/SovereignAI-Starter/README.md @@ -1,6 +1,6 @@ # SovereignAI Starter -مشروع تعليمي لبناء مساعد ذكاء اصطناعي محلي قابل للتوسع. النموذج المحمّل حاليًا: `qwen2.5:1.5b-instruct-q4_K_M` (نحو 986 ميغابايت). +مشروع تعليمي لبناء مساعد ذكاء اصطناعي محلي قابل للتوسع. النموذج الافتراضي `qwen2.5:1.5b-instruct-q4_K_M` (نحو 986 ميغابايت)، ونموذج `gemma4:e2b` (نحو 7.16 غيغابايت) مثبت للتجربة والمقارنة. ## مكونات النسخة الأولى - FastAPI كواجهة HTTP محلية. @@ -33,6 +33,15 @@ $env:LOCAL_LLM_BASE_URL = "http://127.0.0.1:11434/v1" $env:LOCAL_MODEL = "qwen2.5:1.5b-instruct-q4_K_M" ``` +لتشغيل API على Gemma 4 بدل النموذج الافتراضي في نافذة PowerShell الحالية: + +```powershell +$env:LOCAL_MODEL = "gemma4:e2b" +.\start-api.ps1 +``` + +يمكن أيضًا اختيار النموذج لكل طلب من `POST /v1/chat/completions` بإرسال `model`، أو من `POST /v1/agent/run` بإرسال `model` مع `task`. يبقى النموذج الافتراضي Qwen ما لم تغيّره من البيئة. على جهاز الاختبار، Gemma 4 E2B اشتغلت محليًا على CPU، لكنها استخدمت قرابة 3.5 غيغابايت من الذاكرة أثناء التحميل؛ قد تختلف السرعة حسب الجهاز. + راجع رخصة النموذج المختار وشروطه الحالية قبل الاستخدام التجاري. تنزيل النموذج يحتاج اتصال إنترنت ومساحة تخزين. 5. شغل الـ API على جهازك فقط (أو شغل `start-api.ps1`): @@ -48,6 +57,7 @@ python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000 - `POST /v1/chat/completions` لإرسال رسالة. - `POST /v1/agent/run` لتجربة الوكيل والأداة الحسابية. - `POST /v1/chat/completions` يمرر الطلب إلى الخادم المحلي عند ضبط المتغيرين أعلاه. +- يمكن التبديل في Swagger بين النموذجين بإضافة `model` إلى جسم الطلب، مثل `gemma4:e2b`. الخدمة مربوطة بـ `127.0.0.1` عمدًا، فلا تعرضها على الشبكة أو الإنترنت الآن. الوكيل الحالي يمرر السؤال النصي للنموذج، وينفذ الحسابات البسيطة بأداة حساب محلية محدودة. لا يقرأ ملفات ولا ينفذ أوامر النظام. diff --git a/SovereignAI-Starter/app/main.py b/SovereignAI-Starter/app/main.py index e5d618f..8be2b7e 100644 --- a/SovereignAI-Starter/app/main.py +++ b/SovereignAI-Starter/app/main.py @@ -47,6 +47,10 @@ class ChatRequest(BaseModel): class AgentRequest(BaseModel): task: str = Field(description="مهمة قصيرة للوكيل المحلي") + model: str | None = Field( + default=None, + description="اسم نموذج Ollama؛ اتركه فارغًا لاستخدام النموذج الافتراضي", + ) class StoredMessage(BaseModel): @@ -106,7 +110,7 @@ def get_local_user() -> dict[str, str]: def chat_payload(request: ChatRequest, *, stream: bool) -> dict[str, Any]: model = request.model or os.getenv("LOCAL_MODEL", "qwen2.5:1.5b-instruct-q4_K_M") - return { + payload: dict[str, Any] = { "model": model, "messages": ( [message.model_dump() for message in request.messages] @@ -130,6 +134,10 @@ def chat_payload(request: ChatRequest, *, stream: bool) -> dict[str, Any]: ), "stream": stream, } + # Disable Gemma 4 thinking so Ollama places the answer in `content` for clients. + if model.lower().startswith("gemma4"): + payload["reasoning_effort"] = "none" + return payload @app.post("/v1/chat/completions") @@ -283,7 +291,7 @@ async def run_agent(request: AgentRequest) -> dict[str, Any]: pass base_url = os.getenv("LOCAL_LLM_BASE_URL", "http://127.0.0.1:11434/v1").rstrip("/") - model = os.getenv("LOCAL_MODEL", "qwen2.5:1.5b-instruct-q4_K_M") + model = request.model or os.getenv("LOCAL_MODEL", "qwen2.5:1.5b-instruct-q4_K_M") payload = { "model": model, "messages": [ @@ -292,6 +300,8 @@ async def run_agent(request: AgentRequest) -> dict[str, Any]: ], "stream": False, } + if model.lower().startswith("gemma4"): + payload["reasoning_effort"] = "none" completion = await get_completion(payload, base_url) return { "task": request.task, diff --git a/SovereignAI-Starter/start-api.ps1 b/SovereignAI-Starter/start-api.ps1 index 5cdae98..824b43d 100644 --- a/SovereignAI-Starter/start-api.ps1 +++ b/SovereignAI-Starter/start-api.ps1 @@ -11,7 +11,11 @@ if ($env:GROQ_API_KEY) { Write-Warning 'GROQ_API_KEY is not configured; local chat works, but voice transcription will return 503.' } $ollama = Invoke-RestMethod -Uri 'http://127.0.0.1:11434/api/tags' -TimeoutSec 5 -$model = 'qwen2.5:1.5b-instruct-q4_K_M' +$model = if ($env:LOCAL_MODEL) { + $env:LOCAL_MODEL +} else { + 'qwen2.5:1.5b-instruct-q4_K_M' +} if (-not ($ollama.models.name -contains $model)) { throw "Ollama is running, but model '$model' is missing. Run: ollama pull $model" }