114 lines
4.6 KiB
Python
114 lines
4.6 KiB
Python
"""
|
|
===============================================================================
|
|
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() |