diff --git a/modules/ai_engine/__init__.py b/modules/ai_engine/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/modules/ai_engine/agent.py b/modules/ai_engine/agent.py deleted file mode 100644 index 16f55fb..0000000 --- a/modules/ai_engine/agent.py +++ /dev/null @@ -1,178 +0,0 @@ -""" -=============================================================================== -FILE: modules/ai_engine/agent.py -PROJECT: SCUD Orion AI (Unified Architecture) -MODULE: modules / ai_engine -ROLE: Лаконичный нативный оркестратор Function Calling, диспетчер handlers - и менеджер свободных диалогов (Topic Drift). - -AI-CONTEXT-ANCHORS: - - ANCHOR[AGENT_PIPELINE_ENTRY]: Главная точка входа process_chat_message. -=============================================================================== -""" - -import sys -import json -import logging -from typing import List, Dict, Any, Tuple, Optional - -from modules.web_api.llm.schemas import TOOLS_SCHEMA -from modules.web_api.llm.core.ollama_client import call_ollama_chat -from modules.web_api.llm.core.fast_path import handle_fast_path_intercept -from modules.web_api.llm.core.tool_injector import clean_raw_tool_tags, clean_output, inject_tools_if_needed -from modules.web_api.llm.core.context_manager import mark_last_user_message_ephemeral, close_tool_session_and_cleanup -from modules.web_api.llm.db.db_chat import db_save_chat_message, db_get_chat_history, db_purge_ephemeral_messages -from modules.web_api.llm.db.db_prompts import ( - db_get_session_state, db_clear_session_state, db_set_session_state, - db_get_stats, db_get_anomalies, db_get_reference -) -from services.knowledge.service import get_rules - -from .context_builder import build_agent_system_context -from .handlers.task_handler import handle_tasks_call -from .handlers.prompt_handler import handle_prompt_call -from .handlers.snapshot_handler import handle_snapshots_call - -logger = logging.getLogger("AI_AGENT") -logger.setLevel(logging.INFO) - - -# ANCHOR[AGENT_PIPELINE_ENTRY] -def process_chat_message( - user_id: int, - user_message: str, - file_context: str = "", - image_b64: Optional[str] = None, - chat_history: List[Dict[str, Any]] = None, - session_id: str = "web_session_main" -) -> Tuple[str, List[Dict[str, Any]], Optional[Dict[str, Any]]]: - """Главный конвейер обработки сообщений чата.""" - logger.info(f"Сообщение от user_id={user_id}, session_id={session_id}: {user_message}") - - session_state = db_get_session_state(session_id) - if not session_state: - db_purge_ephemeral_messages(session_id) - - full_user_content = f"{user_message}\n\n[СОДЕРЖИМОЕ ПРИКРЕПЛЕННОГО ФАЙЛА]:\n{file_context}" if file_context else user_message - - # 1. Быстрый Fast-Path перехват кнопок подтверждения - fast_path_res = handle_fast_path_intercept(session_id, user_message, full_user_content, session_state) - if fast_path_res: - return fast_path_res - - db_save_chat_message(session_id, "user", full_user_content, is_ephemeral=0) - db_history = db_get_chat_history(session_id, limit=20) - system_prompt = build_agent_system_context(user_id, session_state) - - user_msg_obj = {"role": "user", "content": full_user_content} - - try: - if image_b64: - user_msg_obj["images"] = [image_b64] - messages = [{"role": "system", "content": "Строгий модуль OCR. Перепиши весь текст буква в букву."}, user_msg_obj] - msg = call_ollama_chat(messages, is_vision=True) - else: - clean_history = [dict(m) for m in db_history] - for m in clean_history: m.pop("images", None) - messages = [{"role": "system", "content": system_prompt}] + clean_history + [user_msg_obj] - msg = call_ollama_chat(messages, tools=TOOLS_SCHEMA, is_vision=False) - - raw_reply = msg.get("content", "") - tool_calls = msg.get("tool_calls", []) - - # 2. Гибридный семантический классификатор намерений (Fallback Safety Net) - tool_calls = inject_tools_if_needed(user_message, raw_reply, tool_calls) - - # 3. Исполнение инструментов через изолированные handlers - if tool_calls: - tool = tool_calls[0] - fn_name = tool["function"]["name"] - fn_args = tool["function"].get("arguments", {}) - if isinstance(fn_args, str): - try: fn_args = json.loads(fn_args) - except Exception: fn_args = {} - - logger.info(f"Вызов инструмента: {fn_name} с аргументами: {fn_args}") - close_tool_session_and_cleanup(session_id, close_reason=f"ACTIVATE_{fn_name}") - mark_last_user_message_ephemeral(session_id) - - state_data = session_state.get("data_json") or {} if session_state else {} - - if fn_name in ["db_get_tasks", "db_tasks_edit", "db_add_task", "db_update_task_status", "db_delete_task"]: - return handle_tasks_call(fn_name, fn_args, user_id, session_id) - - elif fn_name in ["db_get_system_prompt", "db_prompt_node_edit"]: - return handle_prompt_call(fn_name, fn_args, session_id) - - elif fn_name in ["db_get_snapshots", "db_delete_snapshots"]: - return handle_snapshots_call(fn_name, fn_args, session_id, user_message, state_data) - - elif fn_name == "db_get_rules": - res_str = json.dumps(get_rules(), ensure_ascii=False) - elif fn_name == "db_get_stats": - res_str = json.dumps(db_get_stats(), ensure_ascii=False) - elif fn_name == "db_get_anomalies": - res_str = json.dumps(db_get_anomalies(limit=fn_args.get("limit", 100), date_str=fn_args.get("date_str")), ensure_ascii=False) - elif fn_name == "db_get_reference": - res_str = json.dumps(db_get_reference(category=fn_args.get("category")), ensure_ascii=False) - else: - res_str = "{}" - - messages.append(msg) - messages.append({"role": "tool", "content": res_str}) - sec_msg = call_ollama_chat(messages, is_vision=False) - final_content = clean_raw_tool_tags(clean_output(sec_msg.get("content", ""))) or "Запрос выполнен." - db_save_chat_message(session_id, "assistant", final_content, is_ephemeral=0) - return final_content, db_get_chat_history(session_id), None - - # 4. Обычный содержательный диалог и управление Topic Drift - final_reply = clean_raw_tool_tags(clean_output(raw_reply)) or "Запрос обработан." - for artifact in ["почемучка,", "почемучка!", "почемучка?", "почемучка", "почемучто,", "почемучто", "почему-то"]: - if final_reply.lower().startswith(artifact): - final_reply = final_reply[len(artifact):].lstrip(",.!?:; -") - - action_payload = None - - # Обработка ответа "нет / спасибо" в режиме открытого инструмента - if session_state and any(kw in user_message.lower() for kw in ["нет", "спасибо", "не надо", "готово", "хватит"]): - close_tool_session_and_cleanup(session_id, close_reason="USER_DISMISSED_TOOL") - db_save_chat_message(session_id, "assistant", final_reply, is_ephemeral=0) - return final_reply, db_get_chat_history(session_id), None - - # Инкремент счётчика шагов в сторону от инструмента (Topic Drift) - if session_state and session_state.get("state_type") in ["PROMPT_FOLLOWUP", "PROMPT_PREVIEW", "SNAPSHOTS_VIEW"]: - state_type = session_state.get("state_type") - state_data = session_state.get("data_json") or {} - if not isinstance(state_data, dict): - state_data = {} - - idle_turns = state_data.get("idle_turns", 0) + 1 - state_data["idle_turns"] = idle_turns - - if idle_turns >= 4: - # 4-й шаг не по теме: бесшумно закрываем сессию и вычищаем эфемерные карточки - close_tool_session_and_cleanup(session_id, close_reason="TOPIC_DRIFT_TIMEOUT") - elif idle_turns == 3: - # 3-й шаг: выводим вежливое напоминание с кнопками - tool_label = "системным промптом" if "PROMPT" in state_type else "снапшотами СКУД" - guard_question = f"Желаете продолжить работу с {tool_label}?" - final_reply += f"\n\n💡 *Напоминание:* {guard_question}" - action_payload = { - "type": "FOLLOW_UP_ACTION", - "buttons": [ - {"label": "Показать снова", "value": "покажи системный промпт" if "PROMPT" in state_type else "покажи снапшоты", "style": "primary"}, - {"label": "Завершить", "value": "нет, спасибо", "style": "secondary"} - ] - } - db_set_session_state(session_id, state_type, state_data) - else: - # 1-й и 2-й шаг: фиксируем обновленный счётчик - db_set_session_state(session_id, state_type, state_data) - - is_ephem_reply = 1 if "актуальный системный промпт:" in final_reply.lower() else 0 - db_save_chat_message(session_id, "assistant", final_reply, is_ephemeral=is_ephem_reply) - return final_reply, db_get_chat_history(session_id), action_payload - - except Exception as ex: - logger.exception(f"Ошибка в агенте: {ex}") - return f"Внутренняя ошибка сервера: {ex}", db_get_chat_history(session_id), None \ No newline at end of file diff --git a/modules/ai_engine/context_builder.py b/modules/ai_engine/context_builder.py deleted file mode 100644 index 5b0fd48..0000000 --- a/modules/ai_engine/context_builder.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -=============================================================================== -FILE: modules/ai_engine/context_builder.py -PROJECT: SCUD Orion AI (Unified Architecture) -MODULE: modules / ai_engine -ROLE: Динамическая сборка системного промпта из БД, календаря и контекста сессий. - -AI-CONTEXT-ANCHORS: - - ANCHOR[CONTEXT_BUILDER_MAIN]: Формирование полного системного контекста для LLM. -=============================================================================== -""" - -from typing import Dict, Any, Optional -from services.prompts.service import get_active_system_prompt -from modules.web_api.llm.core.calendar_utils import get_dynamic_calendar_context - - -# ANCHOR[CONTEXT_BUILDER_MAIN] -def build_agent_system_context( - user_id: int, - session_state: Optional[Dict[str, Any]] -) -> str: - """Формирует полный динамический системный контекст агента с правилами из БД.""" - - # 1. Загружаем актуальный базовый системный промпт из SQLite БД - base_system_prompt = get_active_system_prompt() - - # 2. Серверный календарь и контекст дат - calendar_context = get_dynamic_calendar_context() - user_info = f"Пользователь ID={user_id}" if user_id != 0 else "Гость" - - # 3. Контекст текущего активного стейта сессии - current_state_type = session_state.get("state_type") if session_state else None - state_data = session_state.get("data_json") or {} if session_state else {} - if not isinstance(state_data, dict): - state_data = {} - - active_state_context = "" - if current_state_type == "PROMPT_PREVIEW": - active_state_context = ( - "\n[ТЕКУЩИЙ РЕЖИМ: ПРЕДПРОСМОТР СИСТЕМНОГО ПРОМПТА]\n" - "- Открыт предпросмотр изменений промпта. Для любых правок вызывай db_prompt_node_edit.\n" - ) - elif current_state_type == "PROMPT_FOLLOWUP": - active_state_context = ( - "\n[ТЕКУЩИЙ РЕЖИМ: СЕССИЯ РЕДАКТИРОВАНИЯ ПРОМПТА]\n" - "- Оператор просматривает или редактирует системный промпт.\n" - "- На любые команды вида 'удали пункт X.Y' или 'удали X.Y' ТЫ ОБЯЗАН ВЫЗВАТЬ db_prompt_node_edit с action='DELETE', section_id=X, item_id=Y.\n" - "- На любые команды 'добавь пункт X.Y ...' вызывай action='ADD'.\n" - "- Запрещено путать ADD и DELETE.\n" - ) - elif current_state_type == "SNAPSHOTS_VIEW": - active_date = state_data.get("query_date", "выбранную дату") - active_state_context = ( - f"\n[ТЕКУЩИЙ РЕЖИМ: ПРОСМОТР СНАПШОТОВ СКУД]\n" - f"- Сейчас на экране отображаются снапшоты за {active_date}.\n" - f"- Ты можешь форматировать, фильтровать или анализировать этот текущий срез.\n" - f"- Если оператор запрашивает ДРУГУЮ дату или день недели (например: 'за вчера', 'а за 13.08') — " - f"ТЫ ОБЯЗАН СРАЗУ ВЫЗВАТЬ db_get_snapshots(date_str=...).\n" - f"- Запрещено генерировать текст за другую дату по памяти.\n" - ) - elif current_state_type == "SNAPSHOT_DELETE_CONFIRM": - active_state_context = "\n[ВНИМАНИЕ: ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ УДАЛЕНИЯ СНАПШОТОВ]\n" - elif current_state_type == "TASK_DELETE_CONFIRM": - active_state_context = "\n[ВНИМАНИЕ: ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ УДАЛЕНИЯ ЗАДАЧИ]\n" - - # 4. Сборка полного системного сообщения - return ( - f"{base_system_prompt}\n\n" - f"СТРОГИЕ ПРАВИЛА ВЫЗОВА ИНСТРУМЕНТОВ:\n" - f"1. Для системного промпта:\n" - f" - 'добавь X.Y Текст' -> СРАЗУ вызывай db_prompt_node_edit(action='ADD', section_id=X, item_id=Y, content='Текст').\n" - f" - 'удали X.Y' -> СРАЗУ вызывай db_prompt_node_edit(action='DELETE', section_id=X, item_id=Y, content='').\n" - f" - 'покажи системный промпт' -> СРАЗУ вызывай db_get_system_prompt().\n" - f"2. Для задач:\n" - f" - 'удали задачу N' -> СРАЗУ вызывай db_tasks_edit(action='DELETE', task_id='N').\n" - f" - 'возьми в работу N' -> СРАЗУ вызывай db_tasks_edit(action='UPDATE', task_id='N', status='IN_PROGRESS').\n" - f" - 'заверши N' / 'готово N' -> СРАЗУ вызывай db_tasks_edit(action='UPDATE', task_id='N', status='COMPLETED').\n" - f"3. Запрещено задавать вопросы и писать подтверждения текстом — СРАЗУ вызывай соответствующий инструмент!\n\n" - f"[СИСТЕМНЫЙ КАЛЕНДАРЬ СЕРВЕРА]\n" - f"- Пользователь: {user_info}\n" - f"- {calendar_context}\n" - f"{active_state_context}" - ) \ No newline at end of file diff --git a/modules/ai_engine/handlers/__init__.py b/modules/ai_engine/handlers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/modules/ai_engine/handlers/prompt_handler.py b/modules/ai_engine/handlers/prompt_handler.py deleted file mode 100644 index 02ca6a8..0000000 --- a/modules/ai_engine/handlers/prompt_handler.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -=============================================================================== -FILE: modules/ai_engine/handlers/prompt_handler.py -PROJECT: SCUD Orion AI (Unified Architecture) -MODULE: modules / ai_engine / handlers -ROLE: Изолированная обработка команд управления системным промптом. -=============================================================================== -""" - -from typing import Dict, Any, Tuple, Optional -from services.prompts.service import get_active_system_prompt, create_prompt_preview -from modules.web_api.llm.db.db_chat import db_save_chat_message, db_get_chat_history -from modules.web_api.llm.db.db_prompts import db_set_session_state - - -# ANCHOR[PROMPT_HANDLER_DISPATCH] -def handle_prompt_call( - fn_name: str, - fn_args: Dict[str, Any], - session_id: str -) -> Tuple[str, list, Optional[Dict[str, Any]]]: - """Обрабатывает вызовы просмотра и изменения системного промпта.""" - - # 1. Просмотр промпта с кнопкой быстрого перехода в редактор - if fn_name == "db_get_system_prompt": - active_prompt = get_active_system_prompt() - reply_text = f"Актуальный системный промпт:\n\n{active_prompt}" - - # Сохраняем состояние сессии для возможности мгновенного редактирования и подтверждения - db_set_session_state(session_id, "PROMPT_PREVIEW", { - "draft_text": active_prompt, - "action": "MANUAL_EDIT", - "idle_turns": 0 - }) - db_save_chat_message(session_id, "assistant", reply_text, is_ephemeral=1) - - return reply_text, db_get_chat_history(session_id), { - "type": "PROMPT_PREVIEW", - "raw_draft": active_prompt, - "baseline_prompt": active_prompt, - "buttons": [ - {"label": "✏️ Редактировать", "value": "action:open_editor", "style": "secondary"}, - {"label": "Готово", "value": "нет, спасибо", "style": "secondary"} - ] - } - - # 2. Предпросмотр точечных или пакетных изменений (ADD / EDIT / DELETE / BATCH_DELETE) - action = str(fn_args.get("action", "ADD")).upper() - nodes_list = fn_args.get("nodes_list", []) - delete_nodes_tuples = [] - - if nodes_list: - for n_str in nodes_list: - parts = str(n_str).strip().split(".") - if len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit(): - delete_nodes_tuples.append((int(parts[0]), int(parts[1]))) - - sec_id = None - itm_id = None - try: - if fn_args.get("section_id") is not None: - sec_id = int(str(fn_args.get("section_id")).strip()) - if fn_args.get("item_id") is not None: - itm_id = int(str(fn_args.get("item_id")).strip()) - except Exception: - pass - - content = str(fn_args.get("content", "")).strip() - - merged_prompt, diff_html, baseline_prompt = create_prompt_preview( - action=action, - section_id=sec_id, - item_id=itm_id, - content=content, - delete_nodes=delete_nodes_tuples if delete_nodes_tuples else None - ) - - db_set_session_state(session_id, "PROMPT_PREVIEW", { - "draft_text": merged_prompt, - "action": "MANUAL_EDIT", - "section_id": sec_id, - "item_id": itm_id, - "content": content, - "idle_turns": 0 - }) - - preview_reply = f"Предпросмотр изменений системного промпта:\n\n{diff_html}\n\nДля применения подтвердите действие, отредактируйте или отмените." - db_save_chat_message(session_id, "assistant", preview_reply, is_ephemeral=1) - return preview_reply, db_get_chat_history(session_id), { - "type": "PROMPT_PREVIEW", - "raw_draft": merged_prompt, - "baseline_prompt": baseline_prompt, - "buttons": [ - {"label": "Подтвердить", "value": "подтверждаю", "style": "primary"}, - {"label": "Отменить", "value": "отмена", "style": "danger"}, - {"label": "✏️ Редактировать", "value": "action:open_editor", "style": "secondary"} - ] - } \ No newline at end of file diff --git a/modules/ai_engine/handlers/snapshot_handler.py b/modules/ai_engine/handlers/snapshot_handler.py deleted file mode 100644 index 3b7b425..0000000 --- a/modules/ai_engine/handlers/snapshot_handler.py +++ /dev/null @@ -1,91 +0,0 @@ -""" -=============================================================================== -FILE: modules/ai_engine/handlers/snapshot_handler.py -PROJECT: SCUD Orion AI (Unified Architecture) -MODULE: modules / ai_engine / handlers -ROLE: Изолированная обработка запросов просмотра и удаления срезов СКУД. - -AI-CONTEXT-ANCHORS: - - ANCHOR[SNAPSHOT_HANDLER_DISPATCH]: Обработка db_get_snapshots и db_delete_snapshots. -=============================================================================== -""" - -from typing import Dict, Any, Tuple, Optional, List -from services.snapshots.service import get_snapshots_registry, delete_snapshots_safely -from modules.web_api.llm.core.calendar_utils import parse_relative_date_ru -from modules.web_api.llm.db.db_chat import db_save_chat_message, db_get_chat_history -from modules.web_api.llm.db.db_prompts import db_set_session_state - - -# ANCHOR[SNAPSHOT_HANDLER_DISPATCH] -def handle_snapshots_call( - fn_name: str, - fn_args: Dict[str, Any], - session_id: str, - user_message: str, - state_data: Dict[str, Any] -) -> Tuple[str, list, Optional[Dict[str, Any]]]: - """Обрабатывает запросы реестра и безопасного удаления срезов.""" - - # 1. Получение срезов - if fn_name == "db_get_snapshots": - date_param = fn_args.get("date_str") - if not date_param and user_message: - date_param = parse_relative_date_ru(user_message) - - snapshots_res = get_snapshots_registry(date_str=date_param) - query_date = snapshots_res.get("query_date", "выбранную дату") - - db_set_session_state(session_id, "SNAPSHOTS_VIEW", snapshots_res) - reply_text = f"Реестр срезов СКУД за {query_date}:" - db_save_chat_message(session_id, "assistant", reply_text, is_ephemeral=1) - return reply_text, db_get_chat_history(session_id), { - "type": "SNAPSHOTS_CARD", - "data": snapshots_res - } - - # 2. Удаление срезов (двухфазное подтверждение) - raw_id = fn_args.get("snapshot_id") or fn_args.get("day_str") - raw_ids = fn_args.get("snapshot_ids") or [] - is_confirmed = fn_args.get("confirmed", False) - - if raw_id and not raw_ids: - if isinstance(raw_id, str) and "," in raw_id: - raw_ids = [s.strip() for s in raw_id.split(",")] - else: - raw_ids = [raw_id] - - safe_ids = [s.strip() for s in raw_ids if s and not str(s).strip().startswith("Y")] - if not safe_ids: - reply_text = "⚠️ Итоговый срез Y защищен от удаления. Выберите дневные снапшоты." - db_save_chat_message(session_id, "assistant", reply_text, is_ephemeral=1) - return reply_text, db_get_chat_history(session_id), None - - if not is_confirmed: - query_date = state_data.get("query_date", "") - db_set_session_state(session_id, "SNAPSHOT_DELETE_CONFIRM", { - "snapshot_ids": safe_ids, - "query_date": query_date, - "idle_turns": 0 - }) - ids_str = ", ".join(safe_ids) - reply_text = f"Вы действительно хотите удалить дневные снапшоты: {ids_str}?" - db_save_chat_message(session_id, "assistant", reply_text, is_ephemeral=1) - return reply_text, db_get_chat_history(session_id), { - "type": "SNAPSHOT_DELETE_CONFIRM", - "buttons": [ - {"label": f"Удалить ({len(safe_ids)} шт.)", "value": f"подтверждаю удаление снапшотов {ids_str}", "style": "danger"}, - {"label": "Отмена", "value": "отмена", "style": "secondary"} - ] - } - else: - delete_snapshots_safely(snapshot_ids=safe_ids) - query_date = state_data.get("query_date", "") - updated_data = get_snapshots_registry(date_str=query_date) - - reply_text = f"✅ Успешно удалено снапшотов: {len(safe_ids)} шт." - db_save_chat_message(session_id, "assistant", reply_text, is_ephemeral=1) - return reply_text, db_get_chat_history(session_id), { - "type": "SNAPSHOTS_CARD", - "data": updated_data - } \ No newline at end of file diff --git a/modules/ai_engine/handlers/task_handler.py b/modules/ai_engine/handlers/task_handler.py deleted file mode 100644 index f5d7e39..0000000 --- a/modules/ai_engine/handlers/task_handler.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -=============================================================================== -FILE: modules/ai_engine/handlers/task_handler.py -PROJECT: SCUD Orion AI (Unified Architecture) -MODULE: modules / ai_engine / handlers -ROLE: Изолированная обработка вызовов инструментов задач (get, edit, delete, export). - -AI-CONTEXT-ANCHORS: - - ANCHOR[TASK_HANDLER_DISPATCH]: Обработка вызовов db_get_tasks и db_tasks_edit. -=============================================================================== -""" - -from typing import Dict, Any, Tuple, Optional -from services.tasks.service import get_tasks, execute_task_action -from modules.web_api.llm.db.db_chat import db_save_chat_message, db_get_chat_history -from modules.web_api.llm.db.db_prompts import db_set_session_state - - -# ANCHOR[TASK_HANDLER_DISPATCH] -def handle_tasks_call( - fn_name: str, - fn_args: Dict[str, Any], - user_id: int, - session_id: str -) -> Tuple[str, list, Optional[Dict[str, Any]]]: - """Обрабатывает нативные tool-вызовы по задачам.""" - - # 1. Просмотр задач - if fn_name == "db_get_tasks": - raw_tasks = get_tasks(user_id, status=fn_args.get("status")) - reply_text = "Вот интерактивный список ваших текущих задач:" - db_save_chat_message(session_id, "assistant", reply_text, is_ephemeral=1) - return reply_text, db_get_chat_history(session_id), { - "type": "TASK_INTERACTIVE_CARD", - "tasks": raw_tasks - } - - # 2. Модификация / Удаление / Экспорт - action = (fn_args.get("action") or "UPDATE").upper() - if fn_name == "db_add_task": action = "ADD" - elif fn_name == "db_delete_task": action = "DELETE" - elif fn_name == "db_update_task_status": action = "UPDATE" - - # Двухфазное подтверждение удаления - if action == "DELETE": - task_id_raw = str(fn_args.get("task_id", "")).replace("#", "").replace("TASK-", "").strip() - db_set_session_state(session_id, "TASK_DELETE_CONFIRM", {"task_id": task_id_raw, "idle_turns": 0}) - reply_text = f"Вы действительно хотите удалить задачу #{task_id_raw}?" - db_save_chat_message(session_id, "assistant", reply_text, is_ephemeral=1) - return reply_text, db_get_chat_history(session_id), { - "type": "TASK_DELETE_CONFIRM", - "buttons": [ - {"label": f"Удалить #{task_id_raw}", "value": f"подтверждаю удаление задачи {task_id_raw}", "style": "danger"}, - {"label": "Отмена", "value": "отмена", "style": "secondary"} - ] - } - - # Экспорт в Markdown - elif action == "EXPORT": - export_res = execute_task_action( - user_id=user_id, - action="EXPORT", - filename=fn_args.get("filename"), - status=fn_args.get("status") - ) - reply_text = export_res.get("message", "Отчет по задачам успешно экспортирован.") - db_save_chat_message(session_id, "assistant", reply_text, is_ephemeral=0) - - action_payload = None - if export_res.get("status") == "success": - action_payload = { - "type": "FILE_DOWNLOAD_CARD", - "filename": export_res.get("filename"), - "download_url": export_res.get("download_url"), - "tasks_count": export_res.get("tasks_count") - } - return reply_text, db_get_chat_history(session_id), action_payload - - # Создание и обновление - else: - res = execute_task_action( - user_id=user_id, - action=action, - task_id=fn_args.get("task_id"), - title=fn_args.get("title"), - priority=fn_args.get("priority", "MEDIUM"), - status=fn_args.get("status"), - module=fn_args.get("module", "general"), - due_date=fn_args.get("due_date") - ) - raw_tasks = get_tasks(user_id) - reply_text = res.get("message", "Операция над задачами выполнена.") - db_save_chat_message(session_id, "assistant", reply_text, is_ephemeral=1) - return reply_text, db_get_chat_history(session_id), { - "type": "TASK_INTERACTIVE_CARD", - "tasks": raw_tasks - } \ No newline at end of file diff --git a/modules/web_api/llm/db/connection.py b/modules/web_api/llm/db/connection.py index fee40db..482f202 100644 --- a/modules/web_api/llm/db/connection.py +++ b/modules/web_api/llm/db/connection.py @@ -1,17 +1,8 @@ """ +=============================================================================== FILE: modules/web_api/llm/db/connection.py +ROLE: Реэкспорт единого подключения к БД из core.connection. +=============================================================================== """ -import os -import sqlite3 -# Динамический путь к общей БД в корне проекта -BASE_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../")) -DB_PATH = os.path.join(BASE_ROOT, "data", "scud_orion_ai.db") - -def get_db_connection() -> sqlite3.Connection: - conn = sqlite3.connect(DB_PATH, timeout=30.0) - conn.row_factory = sqlite3.Row - conn.execute("PRAGMA foreign_keys = ON;") - conn.execute("PRAGMA journal_mode = WAL;") - conn.execute("PRAGMA synchronous = NORMAL;") - return conn \ No newline at end of file +from core.connection import get_connection as get_db_connection, DB_PATH \ No newline at end of file diff --git a/scripts/cron/run_reports_only.sh b/scripts/cron/run_reports_only.sh index 0493f93..299cc46 100755 --- a/scripts/cron/run_reports_only.sh +++ b/scripts/cron/run_reports_only.sh @@ -5,5 +5,5 @@ cd /home/puh/projects/scud_ai mkdir -p /home/puh/projects/scud_ai/logs echo "[CRON REPORTS START] $(date '+%Y-%m-%d %H:%M:%S')" >> /home/puh/projects/scud_ai/logs/cron_reports.log -/home/puh/scud_orion_ai_v2/venv/bin/python /home/puh/projects/scud_ai/main_etl.py --use-existing-snapshot >> /home/puh/projects/scud_ai/logs/cron_reports.log 2>&1 +/home/puh/scud_orion_ai_v2/venv/bin/python /home/puh/projects/scud_ai/main_etl.py --skip-export >> /home/puh/projects/scud_ai/logs/cron_reports.log 2>&1 echo "[CRON REPORTS FINISH] $(date '+%Y-%m-%d %H:%M:%S')" >> /home/puh/projects/scud_ai/logs/cron_reports.log \ No newline at end of file