From 4f6353cf88143e55bb3da04ba2cb2e3e72d605f6 Mon Sep 17 00:00:00 2001 From: Hamza Ayed Date: Sun, 4 Oct 2026 15:04:35 +0300 Subject: [PATCH] fix agent inference timeout on slow local models --- SovereignAI-Starter/ROADMAP.md | 4 +++- SovereignAI-Starter/app/main.py | 4 +++- .../tests/test_timeouts_and_cancellation.py | 18 +++++++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/SovereignAI-Starter/ROADMAP.md b/SovereignAI-Starter/ROADMAP.md index 805be63..d4f65d4 100644 --- a/SovereignAI-Starter/ROADMAP.md +++ b/SovereignAI-Starter/ROADMAP.md @@ -4,6 +4,8 @@ ## الحالة الحالية — 2026-10-04 +- 2026-10-04 — إصلاح سبب انتهاء مهلة الوكيل بعد عدة دقائق: بعد إضافة SSE keep-alive، كشف الفحص أن كل استدعاء نموذج داخل حلقة الوكيل ما زال يستخدم المهلة الافتراضية 180 ثانية، رغم أن العميل والبث يسمحان حتى 10 دقائق. رُفعت مهلة كل خطوة نموذج في الوكيل إلى 600 ثانية لتتسق مع مهلة البث، وأضيف اختبار يثبت قيمة المهلة. بعد إعادة تشغيل Uvicorn للمشروع على 8000، نجحت اختبارات `test_timeouts_and_cancellation` 5/5، وأعاد `/health` الحالة `ok`. طلب وكيل حي أعاد HTTP 200 واكتمل بعد 41.9 ثانية مع نبضتي keep-alive؛ طلب حي ثانٍ اكتمل بعد 12.2 ثانية مع أحداث `done` وإغلاق الجلسة. لا يثبت هذا اختبارًا يستغرق خمس دقائق فعليًا، لكنه يزيل حد الـ180 ثانية الذي كان يطابق الانقطاع المبلغ عنه؛ ما زال اختبار نافذة Flutter التفاعلي غير منجز. + - 2026-10-04 — إصلاح بقاء اتصال بث الوكيل: ظهر أن SSE لا يرسل أي بايت أثناء انتظار استجابة نموذج بطيئة، مع أن مهلة Flutter عشر دقائق؛ أضيف تعليق `: keep-alive` كل 15 ثانية عند خمول البث. اختبار حتمي يحاكي انتظار النموذج وإلغاء العميل، ومجموعة `test_timeouts_and_cancellation.py` نجحت 4/4. أُعيد تشغيل API على 8000 من الشفرة الحالية؛ `/health` أعاد 200، وطلب وكيل حي اختار الحاسبة وأعاد `5.0`، وظهر تعليقان keep-alive قبل اكتمال الرد ثم نجح تسجيل الخروج. هذا يعالج إغلاق الاتصالات الخاملة على المسارات الوسيطة، ولا يثبت سرعة النموذج نفسه. - 2026-10-04 — إصلاح بطء فهرسة PDF الممسوح الذي كان يوقف حلقة FastAPI: نقل تحليل الصفحات والرسم وOCR إلى worker threads؛ صار فهرس Flutter يرسل ملفًا واحدًا في كل مرة بمهلة 5 دقائق ويعرض اسم الملف ورقمه. أثناء OCR حي استغرق 155.21 ثانية، وظل `/health` يستجيب خلال 0.02 ثانية، واكتمل OCR للصفحة وفهرستها دلاليًا. اجتاز اختبار PDF المختلط 1/1؛ واجتازت Flutter 22/22 و`flutter analyze --no-pub` بلا ملاحظات، واختبارات مهلة الوكيل 4/4. لم يكتمل بناء Windows Debug بهذه التغييرات في هذه الجولة: نسخة التطوير المؤقتة تعطلت بسبب مسار تضمين `flutter_secure_storage_windows` في MSBuild/الرابط الرمزي؛ لم يتغير كاش الحزمة أو مصدرها. @@ -14,7 +16,7 @@ - فحص إعادة بناء Windows (2026-10-04): تعذر على Flutter فتح `C:\flutter\flutter_windows_3.27.3-stable\flutter\bin\cache\lockfile`. عملية Flutter قديمة/أخرى ما زالت تستخدم SDK؛ أُوقفت محاولة البناء المؤقتة بعد ثبوت عدم وصولها إلى إنشاء plugins أو MSBuild، ولم يُحذف ملف القفل أو يُقتل أي process غير موثوق. يجب تحرير القفل عبر العملية المالكة أو استخدام SDK منفصل قبل متابعة Windows Debug. -- 2026-10-04 — بناء وتشغيل Windows Debug من SDK معزول: نسخت Flutter 3.29.3 إلى Temp لتجاوز قفل الـSDK الأصلي دون إيقاف العملية المالكة. أزلت CMake cache المنسوخ من workspace المؤقت فقط، ثم نجح `flutter build windows --debug --no-pub` خلال 364.8 ثانية. شُغّل التطبيق PID 43856 بعنوان `Mithqal AI` و`Responding=True`؛ سجل API 8000 نجاح `/v1/auth/local-session` و`/v1/models` و`/v1/conversations` و`/v1/agent/skills`. اجتازت نسخة المصدر نفسها اختبارات Flutter 22/22 و`flutter analyze --no-pub` بلا ملاحظات. نافذة Debug ما زالت مفتوحة، لكن تفاعل المستخدم مع أزرار المشروع/الوكيل لم يُفحص بعد. +- 2026-10-04 — بناء وتشغيل Windows Debug من SDK معزول: نسخت Flutter 3.29.3 إلى Temp لتجاوز قفل الـSDK الأصلي دون إيقاف العملية المالكة. أزلت CMake cache المنسوخ من workspace المؤقت فقط، ثم نجح `flutter build windows --debug --no-pub` خلال 364.8 ثانية. شُغّل التطبيق PID 43856 بعنوان `Mithqal AI` و`Responding=True`؛ سجل API 8000 نجاح `/v1/auth/local-session` و`/v1/models` و`/v1/conversations` و`/v1/agent/skills`. اجتازت نسخة المصدر نفسها اختبارات Flutter 22/22 و`flutter analyze --no-pub` بلا ملاحظات. عند الإطلاق كان HWND غير صفري و`Responding=True`؛ في الفحص اللاحق ظل process يعمل لكنه أعاد HWND=0، لذلك لا أعد النافذة مرئية حاليًا. تفاعل أزرار المشروع/الوكيل لم يُفحص بعد. - 2026-10-04 — تدقيق حالة «العمل على مشروع»: تأكد وجود إنشاء مجلد جديد أو تسجيل مجلد قائم من Flutter، وربطه بحساب المستخدم في SQLite، وفتح الملفات والبحث فيها واقتراح إنشاء/تعديل ملف عبر diff وموافقة صريحة. التطبيق وFastAPI/Gemma 4/SQLite يعملون محليًا الآن. الوكيل لا يشغّل بعد أوامر البناء أو الاختبارات؛ prototype AppContainer غير مدمج، وحد التخزين الصلب لم يثبت، لذا التنفيذ الآلي للمشروع ليس مدعومًا. في اختبار الحالتين على API التطبيق وجد الاسترجاع الدليل 2/2 لكن Gemma امتنعت مرة بصياغة عربية جديدة؛ أضيفت الصياغة لاختبار fallback، واختبارات مهارات الوكيل 21/21. أُعيد تشغيل API التطبيق على 8000 من الشفرة الحالية مع قاعدة SQLite نفسها؛ `/health` أكد Gemma 4 وSQLite وبقاء ملف قاعدة المحادثات. تحقق الاختبار الحي من السؤالين: الأول عرض مقتطف `migration.md` مع مصدره، والثاني ذكر الحقول الثلاثة؛ ثم أزال فهرس الاختبار وتسجيل المشروع والجلسة (`evals/results/retrieval_live_api_fix_2026-10-04.json`). التفاصيل في `evals/MODEL_REVIEW_2026-10.md`. diff --git a/SovereignAI-Starter/app/main.py b/SovereignAI-Starter/app/main.py index 6356314..71b1425 100644 --- a/SovereignAI-Starter/app/main.py +++ b/SovereignAI-Starter/app/main.py @@ -2207,7 +2207,9 @@ async def _execute_agent( read_only_tool_results: dict[str, tuple[Any, list[str]]] = {} remaining_tool_calls = MAX_AGENT_TOOL_CALLS - len(steps) for call_index in range(remaining_tool_calls + 1): - completion = await get_completion(current_payload) + # Agent reasoning and tool-selection can take several minutes on a small + # local model. Keep this aligned with the SSE client/request limit below. + completion = await get_completion(current_payload, timeout_seconds=600.0) final_message = completion["choices"][0].get("message", {}) tool_calls = final_message.get("tool_calls") or [] if not tool_calls: diff --git a/SovereignAI-Starter/tests/test_timeouts_and_cancellation.py b/SovereignAI-Starter/tests/test_timeouts_and_cancellation.py index a090a13..2b4834e 100644 --- a/SovereignAI-Starter/tests/test_timeouts_and_cancellation.py +++ b/SovereignAI-Starter/tests/test_timeouts_and_cancellation.py @@ -14,11 +14,27 @@ if not os.environ.get("SOVEREIGNAI_DATA_DIR"): os.environ["SOVEREIGNAI_DATA_DIR"] = _TEST_DATA_DIR.name from app import database -from app.main import AgentRequest, run_agent_stream +from app.main import AgentRequest, _execute_agent, run_agent_stream from app.model_provider import OllamaProvider class TimeoutAndCancellationTests(unittest.IsolatedAsyncioTestCase): + async def test_agent_model_calls_use_ten_minute_timeout(self) -> None: + observed_timeouts: list[float] = [] + + async def fake_completion(_payload, *, timeout_seconds=180.0): + observed_timeouts.append(timeout_seconds) + return {"choices": [{"message": {"content": "تم"}}]} + + with ( + patch("app.main.get_completion", side_effect=fake_completion), + patch("app.main.workspace.selected_root", return_value=None), + ): + result = await _execute_agent(AgentRequest(task="اشرح لي الفكرة")) + + self.assertEqual(result["result"], "تم") + self.assertEqual(observed_timeouts, [600.0]) + async def test_provider_timeout_is_bounded_and_reported_as_504(self) -> None: observed_timeouts: list[float] = []