Перед тем, как исправлять выгрузку скуд с глупым Gemini-Lite
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
===============================================================================
|
||||
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}"
|
||||
)
|
||||
@@ -0,0 +1,98 @@
|
||||
"""
|
||||
===============================================================================
|
||||
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"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
"""
|
||||
===============================================================================
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
===============================================================================
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user