""" =============================================================================== FILE: services/prompts/repository.py PROJECT: SCUD Orion AI (Unified Architecture) MODULE: services / prompts ROLE: Реляционное хранилище узлов системного промпта (таблица system_prompt_nodes). AI-CONTEXT-ANCHORS: - ANCHOR[PROMPT_REPO_GET_ACTIVE]: Сборка активного промпта из узлов БД. - ANCHOR[PROMPT_REPO_APPLY_ACTION]: Точечная вставка / изменение / удаление узла. =============================================================================== """ import re import logging from typing import List, Tuple, Dict, Any from core.connection import get_connection logger = logging.getLogger("PROMPT_REPO") # ANCHOR[PROMPT_REPO_GET_ACTIVE] def repo_get_active_prompt(prompt_name: str = "main_agent") -> str: """Собирает структурированный текст системного промпта из активных узлов.""" with get_connection(row_factory=True) as conn: cursor = conn.cursor() cursor.execute(""" SELECT section_id, item_id, content FROM system_prompt_nodes WHERE prompt_name = ? AND is_active = 1 ORDER BY section_id ASC, item_id ASC """, (prompt_name,)) rows = cursor.fetchall() if not rows: return "Ты — ИИ-ассистент SCUD Orion AI." lines = [] current_section = None for r in rows: sec_id = r["section_id"] itm_id = r["item_id"] content = r["content"] if itm_id == 0: if current_section is not None: lines.append("") lines.append(f"{sec_id}. {content}") current_section = sec_id else: lines.append(f" {sec_id}.{itm_id}. {content}") return "\n".join(lines) # ANCHOR[PROMPT_REPO_APPLY_ACTION] def repo_apply_prompt_action(action: str, section_id: int, item_id: int, content: str = "", prompt_name: str = "main_agent") -> None: """Точечно применяет действие (ADD / UPDATE / DELETE) над узлом промпта.""" with get_connection() as conn: cursor = conn.cursor() action_clean = (action or "").upper() if action_clean in ["ADD", "UPDATE", "EDIT"]: cursor.execute(""" INSERT INTO system_prompt_nodes (prompt_name, section_id, item_id, content, is_active, updated_at) VALUES (?, ?, ?, ?, 1, CURRENT_TIMESTAMP) ON CONFLICT(prompt_name, section_id, item_id) DO UPDATE SET content = excluded.content, is_active = 1, updated_at = CURRENT_TIMESTAMP """, (prompt_name, section_id, item_id, content)) elif action_clean == "DELETE": cursor.execute(""" DELETE FROM system_prompt_nodes WHERE prompt_name = ? AND section_id = ? AND item_id = ? """, (prompt_name, section_id, item_id)) conn.commit() def repo_save_full_prompt(prompt_text: str, prompt_name: str = "main_agent") -> None: """Парсит и полностью перезаписывает все узлы промпта из сырого текста.""" with get_connection() as conn: cursor = conn.cursor() cursor.execute("DELETE FROM system_prompt_nodes WHERE prompt_name = ?", (prompt_name,)) current_sec = 1 current_itm = 0 for raw_line in prompt_text.splitlines(): clean_line = re.sub(r'<[^>]+>', '', raw_line).strip() if not clean_line: continue sub_match = re.match(r'^(\d+)[\.\s]+(\d+)[\.\s\:\-]+(.*)$', clean_line) sec_match = re.match(r'^(\d+)[\.\s\:\-]+(.*)$', clean_line) if sub_match: current_sec = int(sub_match.group(1)) current_itm = int(sub_match.group(2)) content = sub_match.group(3).strip() elif sec_match and not any(c.islower() for c in sec_match.group(2)[:15]): current_sec = int(sec_match.group(1)) current_itm = 0 content = sec_match.group(2).strip() else: current_itm += 1 content = clean_line cursor.execute(""" INSERT OR REPLACE INTO system_prompt_nodes (prompt_name, section_id, item_id, content, is_active) VALUES (?, ?, ?, ?, 1) """, (prompt_name, current_sec, current_itm, content)) conn.commit()