From 8a3f3c749f2d54dd5b19d66ae17a14759d0cc3ac Mon Sep 17 00:00:00 2001 From: manoraga Date: Sun, 9 Aug 2026 10:50:04 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D1=86?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D1=8B=D0=B9=20=D0=BC=D1=83=D0=BB=D1=8C=D1=82?= =?UTF-8?q?=D0=B8=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D1=81=D0=BA=D0=B8=D0=B9=20=D1=80=D0=B5=D0=B6?= =?UTF-8?q?=D0=B8=D0=BC=20(users,=20JWT,=20=D0=B8=D0=B7=D0=BE=D0=BB=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D1=87=D0=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- llm/agent.py | 87 ++++++------ llm/db_tools.py | 191 ++++++++++++------------- static/index.html | 43 ++++-- static/js/app.js | 13 +- static/js/auth.js | 90 +++++++++--- update_index.sh | 350 ++++++++++++++++++++++++++++++++++++++++++++++ venv/pyvenv.cfg | 2 +- 7 files changed, 581 insertions(+), 195 deletions(-) create mode 100755 update_index.sh diff --git a/llm/agent.py b/llm/agent.py index 8a64928..1bd265b 100644 --- a/llm/agent.py +++ b/llm/agent.py @@ -18,21 +18,39 @@ from .schemas import TOOLS_SCHEMA OLLAMA_URL = "http://192.168.11.3:11434/api/chat" MODEL_NAME = "qwen2.5:14b" -def process_chat_message(user_message: str, chat_history: List[Dict[str, Any]] = None) -> Tuple[str, List[Dict[str, Any]]]: +def process_chat_message(user_id: int, user_message: str, chat_history: List[Dict[str, Any]] = None) -> Tuple[str, List[Dict[str, Any]]]: if chat_history is None: chat_history = [] current_now = datetime.now().strftime("%Y-%m-%d %H:%M") user_lower = user_message.lower().strip() - if any(phrase in user_lower for phrase in ["системный промпт", "покажи промпт", "промпт системы", "промпт из базы"]): - prompt_text = db_get_active_system_prompt() - formatted_text = f"⚙️ Актуальный системный промпт ИИ (из базы SQLite):\n\n{prompt_text}" + # Быстрый прямой ответ для списка задач текущего пользователя + if any(phrase in user_lower for phrase in ["все задачи", "покажи задачи", "список задач", "реестр задач"]): + tasks = db_get_tasks(user_id) + if not tasks: + formatted_text = "Ваш список задач пуст." + else: + def get_task_num(t): + tid = str(t.get("task_id", "")) + try: + return int(tid.upper().replace("TASK-", "").strip()) + except ValueError: + return 0 + + tasks_sorted = sorted(tasks, key=get_task_num, reverse=True) + lines = [f"📋 Ваш реестр задач ({len(tasks_sorted)}):\n"] + for t in tasks_sorted: + tid = t.get('task_id', '') + title = t.get('title', 'Без названия') + lines.append(f"• {tid}: {title}") + formatted_text = "\n".join(lines) + return formatted_text, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": formatted_text}] - if any(phrase in user_lower for phrase in ["базу знаний", "база знаний", "покажи правила", "инструкции ии"]): - rules = db_get_rules() - formatted_text = "🧠 База знаний ИИ (ai_knowledge_base):\n\n" + "\n\n".join([f"• {r.get('id')}. {r.get('rule_text')}" for r in rules]) if rules else "База знаний пуста." + if any(phrase in user_lower for phrase in ["системный промпт", "покажи промпт", "промпт системы"]): + prompt_text = db_get_active_system_prompt() + formatted_text = f"⚙️ Актуальный системный промпт ИИ:\n\n{prompt_text}" return formatted_text, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": formatted_text}] dynamic_prompt_text = db_get_active_system_prompt() @@ -64,64 +82,47 @@ def process_chat_message(user_message: str, chat_history: List[Dict[str, Any]] = tool_calls = msg.get("tool_calls", []) content_str = msg.get("content", "").strip().replace("**", "") - if not tool_calls and "name" in content_str and "db_" in content_str: - try: - start_idx = content_str.find("{") - end_idx = content_str.rfind("}") + 1 - if start_idx != -1 and end_idx != -1: - parsed = json.loads(content_str[start_idx:end_idx]) - if "name" in parsed: - tool_calls = [{"function": parsed}] - except Exception: - pass - if tool_calls: for tool in tool_calls: fn_name = tool["function"]["name"] fn_args = tool["function"].get("arguments", {}) if fn_name == "db_get_tasks": - tasks = db_get_tasks() + tasks = db_get_tasks(user_id) if not tasks: - formatted_text = "Список задач пуст." + formatted_text = "Ваш список задач пуст." else: - tasks_sorted = sorted(tasks, key=lambda x: x.get('task_id', ''), reverse=True) - formatted_text = f"📋 Реестр задач SCUD Orion AI (Всего: {len(tasks_sorted)}):\n\n" + def get_task_num(t): + tid = str(t.get("task_id", "")) + try: + return int(tid.upper().replace("TASK-", "").strip()) + except ValueError: + return 0 + + tasks_sorted = sorted(tasks, key=get_task_num, reverse=True) + lines = [f"📋 Ваш реестр задач ({len(tasks_sorted)}):\n"] for t in tasks_sorted: - due = f" (до {t['due_date']})" if t.get('due_date') else "" - formatted_text += f"• {t.get('task_id')} — {t.get('title')}\n" - formatted_text += f" - Приоритет: {t.get('priority') or 'MEDIUM'} | Статус: {t.get('status') or 'BACKLOG'} | Модуль: {t.get('module') or 'general'}{due}\n\n" - return formatted_text, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": formatted_text}] + tid = t.get('task_id', '') + title = t.get('title', 'Без названия') + lines.append(f"• {tid}: {title}") + formatted_text = "\n".join(lines) - elif fn_name == "db_get_rules": - rules = db_get_rules() - formatted_text = "🧠 База знаний ИИ (ai_knowledge_base):\n\n" + "\n\n".join([f"• {r.get('id')}. {r.get('rule_text')}" for r in rules]) if rules else "База знаний пуста." - return formatted_text, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": formatted_text}] - - elif fn_name == "db_get_system_prompt": - prompt_text = db_get_active_system_prompt() - formatted_text = f"⚙️ Актуальный системный промпт ИИ (из базы SQLite):\n\n{prompt_text}" - return formatted_text, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": formatted_text}] - - elif fn_name == "db_add_system_prompt": - res = db_add_system_prompt(name=fn_args.get("name", "main_agent"), prompt_text=fn_args.get("prompt_text", "")) - formatted_text = res.get("message", "Промпт обновлен.") return formatted_text, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": formatted_text}] elif fn_name == "db_add_task": - res = db_add_task(module=fn_args.get("module", "general"), title=fn_args.get("title"), priority=fn_args.get("priority", "MEDIUM"), due_date=fn_args.get("due_date")) + res = db_add_task(user_id=user_id, module=fn_args.get("module", "general"), title=fn_args.get("title"), priority=fn_args.get("priority", "MEDIUM"), due_date=fn_args.get("due_date")) formatted_text = f"[✓] Задача {res.get('task_id', 'TASK')} успешно создана!" if "status" in res else f"❌ Ошибка: {res.get('error')}" return formatted_text, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": formatted_text}] elif fn_name == "db_update_task_status": - res = db_update_task_status(task_id=fn_args.get("task_id"), status=fn_args.get("status"), due_date=fn_args.get("due_date")) + res = db_update_task_status(user_id=user_id, task_id=fn_args.get("task_id"), status=fn_args.get("status"), due_date=fn_args.get("due_date")) formatted_text = f"[✓] Статус задачи {fn_args.get('task_id')} обновлен!" if "status" in res else f"❌ Ошибка: {res.get('error')}" return formatted_text, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": formatted_text}] elif fn_name == "db_delete_task": task_id_to_del = fn_args.get("task_id", "").upper() - res = db_delete_task(task_id=task_id_to_del) - formatted_text = f"[✓] Задача {task_id_to_del} успешно удалена из базы!" if "status" in res else f"❌ Ошибка: {res.get('error')}" + res = db_delete_task(user_id=user_id, task_id=task_id_to_del) + formatted_text = f"[✓] Задача {task_id_to_del} успешно удалена из вашей базы!" if "status" in res else f"❌ Ошибка: {res.get('error')}" return formatted_text, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": formatted_text}] return content_str, chat_history + [{"role": "user", "content": user_message}, {"role": "assistant", "content": content_str}] diff --git a/llm/db_tools.py b/llm/db_tools.py index d78149a..5eb80ab 100644 --- a/llm/db_tools.py +++ b/llm/db_tools.py @@ -1,129 +1,110 @@ import sqlite3 -from typing import List, Dict, Any +from typing import List, Dict, Any, Optional -DB_NAME = "/home/puh/scud_orion_ai_v2/data/scud_orion_ai.db" +DB_PATH = "/home/puh/scud_context_api/scud_orion_ai.db" -def db_get_active_system_prompt() -> str: - try: - conn = sqlite3.connect(DB_NAME) - cursor = conn.cursor() - cursor.execute("SELECT prompt_text FROM system_prompts WHERE name = 'main_agent' AND is_active = 1 LIMIT 1") - row = cursor.fetchone() - conn.close() - if row and row[0]: - return row[0].replace("**", "") - except Exception as e: - print(f"Ошибка чтения системного промпта из БД: {e}") +def get_db_connection(): + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + return conn - return "Ты — интеллектуальный ИИ-ассистент и архитектурный координатор проекта SCUD Orion AI." +# === ЗАДАЧИ С ФИЛЬТРАЦИЕЙ ПО USER_ID === -def db_add_system_prompt(name: str = "main_agent", prompt_text: str = "") -> dict: - clean_text = prompt_text.replace("**", "") - conn = sqlite3.connect(DB_NAME) +def db_get_tasks(user_id: int) -> List[Dict[str, Any]]: + conn = get_db_connection() cursor = conn.cursor() cursor.execute(""" - INSERT INTO system_prompts (name, prompt_text, is_active) - VALUES (?, ?, 1) - ON CONFLICT(name) DO UPDATE SET prompt_text=excluded.prompt_text, updated_at=CURRENT_TIMESTAMP - """, (name, clean_text)) + SELECT id, task_id, module, title, priority, status, due_date, created_at + FROM tasks + WHERE user_id = ? + ORDER BY id DESC + """, (user_id,)) + rows = cursor.fetchall() + conn.close() + return [dict(r) for r in rows] + +def db_add_task(user_id: int, module: str, title: str, priority: str = "MEDIUM", due_date: Optional[str] = None) -> Dict[str, Any]: + conn = get_db_connection() + cursor = conn.cursor() + + cursor.execute("SELECT MAX(id) FROM tasks") + max_id = cursor.fetchone()[0] or 0 + new_task_id = f"TASK-{(max_id + 1):02d}" + + cursor.execute(""" + INSERT INTO tasks (task_id, module, title, priority, status, due_date, user_id) + VALUES (?, ?, ?, ?, 'BACKLOG', ?, ?) + """, (new_task_id, module, title, priority.upper(), due_date, user_id)) + conn.commit() conn.close() - return {"status": "success", "message": f"Системный промпт '{name}' успешно обновлен в базе!"} + return {"status": "success", "task_id": new_task_id, "message": f"Задача {new_task_id} создана"} -def db_get_tasks(status: str = None) -> list: - conn = sqlite3.connect(DB_NAME) - conn.row_factory = sqlite3.Row +def db_update_task_status(user_id: int, task_id: str, status: str, due_date: Optional[str] = None) -> Dict[str, Any]: + conn = get_db_connection() cursor = conn.cursor() - if status: - cursor.execute("SELECT * FROM tasks WHERE status = ? ORDER BY task_id ASC", (status.upper(),)) - else: - cursor.execute("SELECT * FROM tasks ORDER BY task_id ASC") - rows = [dict(r) for r in cursor.fetchall()] - conn.close() - return rows - -def db_update_task_status(task_id: str, status: str = None, due_date: str = None) -> dict: - conn = sqlite3.connect(DB_NAME) - cursor = conn.cursor() - updates = [] - params = [] - - if status: - status_upper = status.upper() - updates.append("status = ?") - params.append(status_upper) - if status_upper == "COMPLETED": - updates.append("completed_at = CURRENT_TIMESTAMP") - else: - updates.append("completed_at = NULL") - + + task_id_upper = task_id.upper().strip() + if due_date: - updates.append("due_date = ?") - params.append(due_date) - - if not updates: - conn.close() - return {"error": "Не указаны параметры для обновления"} - - params.append(task_id.upper()) - query = f"UPDATE tasks SET {', '.join(updates)} WHERE task_id = ?" - cursor.execute(query, tuple(params)) + cursor.execute(""" + UPDATE tasks + SET status = ?, due_date = ? + WHERE UPPER(task_id) = ? AND user_id = ? + """, (status.upper(), due_date, task_id_upper, user_id)) + else: + cursor.execute(""" + UPDATE tasks + SET status = ? + WHERE UPPER(task_id) = ? AND user_id = ? + """, (status.upper(), task_id_upper, user_id)) if cursor.rowcount == 0: conn.close() - return {"error": f"Задача {task_id} не найдена"} + return {"error": f"Задача {task_id_upper} не найдена или принадлежит другому пользователю"} conn.commit() conn.close() - return {"status": "success", "message": f"Задача {task_id} обновлена"} + return {"status": "success", "message": f"Статус {task_id_upper} обновлен"} -def db_delete_task(task_id: str) -> dict: - conn = sqlite3.connect(DB_NAME) - cursor = conn.cursor() - cursor.execute("DELETE FROM tasks WHERE task_id = ?", (task_id.upper(),)) - deleted_count = cursor.rowcount - conn.commit() - conn.close() - - if deleted_count == 0: - return {"error": f"Задача {task_id} не найдена"} - return {"status": "success", "message": f"Задача {task_id} удалена"} - -def db_add_task(module: str = "general", title: str = "", priority: str = "MEDIUM", due_date: str = None, task_id: str = None) -> dict: - conn = sqlite3.connect(DB_NAME) +def db_delete_task(user_id: int, task_id: str) -> Dict[str, Any]: + conn = get_db_connection() cursor = conn.cursor() + task_id_upper = task_id.upper().strip() - if not task_id: - cursor.execute("SELECT task_id FROM tasks WHERE task_id LIKE 'TASK-%'") - rows = cursor.fetchall() - max_num = 0 - for r in rows: - try: - num = int(r[0].replace("TASK-", "")) - if num > max_num: - max_num = num - except ValueError: - pass - task_id = f"TASK-{max_num + 1:02d}" - - try: - cursor.execute( - "INSERT INTO tasks (task_id, module, title, status, priority, due_date) VALUES (?, ?, ?, 'BACKLOG', ?, ?)", - (task_id.upper(), module, title, priority.upper(), due_date) - ) - conn.commit() - res = {"status": "success", "message": f"Задача {task_id} создана", "task_id": task_id} - except sqlite3.IntegrityError: - res = {"error": f"Задача с ID {task_id} уже существует"} - finally: + cursor.execute("DELETE FROM tasks WHERE UPPER(task_id) = ? AND user_id = ?", (task_id_upper, user_id)) + + if cursor.rowcount == 0: conn.close() - return res + return {"error": f"Задача {task_id_upper} не найдена или принадлежит другому пользователю"} -def db_get_rules() -> list: - conn = sqlite3.connect(DB_NAME) - conn.row_factory = sqlite3.Row - cursor = conn.cursor() - cursor.execute("SELECT id, rule_text, added_by FROM ai_knowledge_base ORDER BY id") - rows = [dict(r) for r in cursor.fetchall()] + conn.commit() conn.close() - return rows + return {"status": "success", "message": f"Задача {task_id_upper} удалена"} + +# === ОБЩИЕ СИСТЕМНЫЕ РЕСУРСЫ === + +def db_get_active_system_prompt() -> str: + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute("SELECT prompt_text FROM system_prompts WHERE is_active = 1 ORDER BY id DESC LIMIT 1") + row = cursor.fetchone() + conn.close() + return row["prompt_text"] if row else "Ты — ИИ-ассистент SCUD Orion AI." + +def db_add_system_prompt(name: str, prompt_text: str) -> Dict[str, Any]: + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute("UPDATE system_prompts SET is_active = 0") + cursor.execute("INSERT INTO system_prompts (name, prompt_text, is_active) VALUES (?, ?, 1)", (name, prompt_text)) + conn.commit() + conn.close() + return {"status": "success", "message": "Системный промпт обновлен"} + +def db_get_rules() -> List[Dict[str, Any]]: + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute("SELECT id, rule_text FROM ai_knowledge_base ORDER BY id ASC") + rows = cursor.fetchall() + conn.close() + return [dict(r) for r in rows] diff --git a/static/index.html b/static/index.html index e92b28c..be0c22c 100644 --- a/static/index.html +++ b/static/index.html @@ -10,24 +10,30 @@ - +
- +

SCUD Orion AI

-

Авторизация в системе

+

Авторизация в системе

- - + + +
+ +
+ +