diff --git a/services/ai_legal_engine.py b/services/ai_legal_engine.py
index 6c0782f..cbc6e4c 100644
--- a/services/ai_legal_engine.py
+++ b/services/ai_legal_engine.py
@@ -23,7 +23,7 @@ class AILegalEngine:
self.api_key = raw_key.strip("\"'") if raw_key else ""
self.client = None
self.model_name = "gemini-2.5-flash"
- self.deep_model_name = "gemini-2.5-pro"
+ self.deep_model_name = "gemini-2.5-flash"
self._init_sdk_client()
def _init_sdk_client(self):
@@ -111,8 +111,60 @@ class AILegalEngine:
data = await resp.json()
return data["candidates"][0]["content"]["parts"][0]["text"]
+ async def query_notebooklm(self, prompt: str) -> dict:
+ """Прямой запрос к блокноту NotebookLM «Разбор ответов строго по ЖК РФ»."""
+ notebook_id = os.getenv("NOTEBOOKLM_ID", "00ed7d92-1ab2-4159-ac72-1d0c4be21377")
+ nlm_bins = ["/home/matrixhasyou/.local/bin/nlm", "/usr/local/bin/nlm", "nlm"]
+ nlm_bin = next((b for b in nlm_bins if os.path.exists(b)), "nlm")
+
+ cmd = [
+ nlm_bin, "query", "notebook",
+ notebook_id,
+ prompt
+ ]
+ env = os.environ.copy()
+ env["SSL_CERT_FILE"] = "/etc/ssl/certs/ca-certificates.crt"
+
+ try:
+ proc = await asyncio.create_subprocess_exec(
+ *cmd,
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE,
+ env=env
+ )
+ stdout, stderr = await proc.communicate()
+ out_str = stdout.decode('utf-8', errors='ignore').strip()
+ err_str = stderr.decode('utf-8', errors='ignore').strip()
+
+ if proc.returncode == 0 and out_str and "Authentication Error" not in out_str and "ConnectError" not in out_str:
+ # Очистка предупреждений об апдейте nlm
+ if "Update available:" in out_str:
+ out_str = out_str.split("Update available:")[0].strip()
+ return {"success": True, "text": out_str, "source": "notebooklm"}
+ else:
+ return {"success": False, "error": err_str or out_str, "source": "notebooklm"}
+ except Exception as e:
+ return {"success": False, "error": str(e), "source": "notebooklm"}
+
async def query(self, prompt: str, role: str = "resident", audio_path: str = None) -> dict:
- """Основной метод запроса к юридическому ядру."""
+ """Основной метод запроса: NotebookLM (первый приоритет) -> Gemini (fallback)."""
+ start_time = datetime.now()
+
+ # Если вопрос от Админа и нет аудио — пробуем NotebookLM в первую очередь!
+ if role in ("admin", "lawyer") and not audio_path and prompt:
+ nlm_res = await self.query_notebooklm(prompt)
+ if nlm_res.get("success"):
+ latency = (datetime.now() - start_time).total_seconds()
+ return {
+ "success": True,
+ "text": nlm_res["text"],
+ "answer": nlm_res["text"],
+ "role": role,
+ "latency_sec": round(latency, 2),
+ "model": "NotebookLM: Разбор ответов строго по ЖК РФ"
+ }
+
+ # Резервный контур через Gemini API (если сессия блокнота требует продления или жилец)
if not self.api_key:
return {
"success": False,
@@ -121,17 +173,15 @@ class AILegalEngine:
"latency_sec": 0.0
}
- start_time = datetime.now()
system_instruction = (
"Ты — интеллектуальный юридический щит 10-подъездного многоквартирного дома по адресу: г. Ульяновск, пр-кт Ленинского Комсомола, д. 37 (МКД ЛенКом 37).\n"
"Дом ведет системную юридическую защиту прав собственников против нарушений управляющей компании, бездействия надзорных органов и фальсификаций.\n"
f"{self.get_role_instructions(role)}"
)
- model_to_use = self.deep_model_name if role in ("lawyer", "admin") else self.model_name
+ model_to_use = self.model_name
try:
- # Используем прямой REST (наивысшая надежность без конфликтов библиотек)
reply_text = await self._query_rest_api(
prompt=prompt,
system_instruction=system_instruction,
@@ -140,6 +190,12 @@ class AILegalEngine:
)
latency = (datetime.now() - start_time).total_seconds()
+
+ # Если для админа не сработал блокнот, честно добавляем статус
+ if role == "admin" and not audio_path:
+ prefix = "📚 [Источник: База законов РФ / Резервный шлюз Gemini]\n(Сессия NotebookLM требует обновления куков)\n\n"
+ reply_text = prefix + reply_text
+
return {
"success": True,
"text": reply_text.strip(),