""" Этот модуль управляет сохранением и извлечением истории графов запросов, используя SQLite базу данных для хранения данных. """ from typing import Dict, Any, List, Optional import json import sqlite3 class GraphHistoryManager: """ Менеджер для хранения истории графов запросов в SQLite. """ def __init__(self, db_path="graph_history.db"): self.db_path = db_path def _get_connection(self): """Получает соединение с базой данных.""" return sqlite3.connect(self.db_path) def _create_table(self, conn): """Создает таблицу для хранения графов, если она не существует.""" cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS graphs ( id TEXT PRIMARY KEY, messages TEXT, graph_nodes TEXT, graph_edges TEXT, current_node_id TEXT ) """) conn.commit() def _get_max_graph_id(self, conn): """Получает максимальный ID графа из базы данных.""" cursor = conn.cursor() cursor.execute( "SELECT MAX(CAST(SUBSTR(id, 7) AS INTEGER)) FROM graphs WHERE id LIKE 'graph_%'" ) result = cursor.fetchone()[0] return result if result is not None else 0 def save_graph(self, graph_data: Any) -> str: """Сохраняет или обновляет данные графа в базе данных и возвращает ID.""" conn = self._get_connection() cursor = conn.cursor() try: graph_id = graph_data.get("id") if not graph_id: max_graph_id = self._get_max_graph_id(conn) next_graph_id = max_graph_id + 1 graph_id = f"graph_{next_graph_id}" graph_data["id"] = graph_id # Преобразуем структуры данных в JSON-строки для хранения в SQLite messages_json = json.dumps(graph_data.get("messages", [])) graph_nodes_json = json.dumps(graph_data.get("graph_nodes", [])) graph_edges_json = json.dumps(graph_data.get("graph_edges", [])) current_node_id = graph_data.get("current_node_id", "") # Проверяем, существует ли уже запись с таким ID cursor.execute("SELECT id FROM graphs WHERE id = ?", (graph_id, )) existing_record = cursor.fetchone() if existing_record: # Обновляем существующую запись cursor.execute( """ UPDATE graphs SET messages = ?, graph_nodes = ?, graph_edges = ?, current_node_id = ? WHERE id = ? """, (messages_json, graph_nodes_json, graph_edges_json, current_node_id, graph_id)) else: # Вставляем новую запись cursor.execute( """ INSERT INTO graphs (id, messages, graph_nodes, graph_edges, current_node_id) VALUES (?, ?, ?, ?, ?) """, (graph_id, messages_json, graph_nodes_json, graph_edges_json, current_node_id)) conn.commit() print(f"Граф сохранен/обновлен: {graph_id}") return graph_id finally: conn.close() def get_graph(self, graph_id: str) -> Optional[Any]: """Получает данные графа по ID из базы данных.""" conn = self._get_connection() cursor = conn.cursor() try: cursor.execute( "SELECT messages, graph_nodes, graph_edges, current_node_id FROM graphs WHERE id = ?", (graph_id, )) result = cursor.fetchone() if result: messages_json, graph_nodes_json, graph_edges_json, current_node_id = result # Преобразуем JSON-строки обратно в структуры данных Python messages = json.loads(messages_json) graph_nodes = json.loads(graph_nodes_json) graph_edges = json.loads(graph_edges_json) return { "id": graph_id, "messages": messages, "graph_nodes": graph_nodes, "graph_edges": graph_edges, "current_node_id": current_node_id } else: return None finally: conn.close() def get_all_graphs_summary(self) -> List[Dict[str, str]]: """Возвращает краткий список всех сохраненных графов.""" conn = self._get_connection() cursor = conn.cursor() try: cursor.execute("SELECT id, messages FROM graphs") graphs_data = cursor.fetchall() return [{ "id": gid, "first_message": json.loads(messages)[0].get("content", "No content") if messages else "No content" } for gid, messages in graphs_data] finally: conn.close() def get_messages_from_root_to_node( self, graph_data: Dict[str, Any], target_node_id: str) -> List[Dict[str, str]]: """Получает список сообщений от корневого узла до указанного узла.""" nodes = graph_data.get("graph_nodes", []) edges = graph_data.get("graph_edges", []) messages = graph_data.get("messages", []) # Создаем словарь для быстрого поиска родительских узлов parent_map = {edge['target']: edge['source'] for edge in edges} # Функция для рекурсивного подъема по дереву до корневого узла def get_path_to_root(node_id: str) -> List[str]: path = [node_id] while node_id in parent_map: node_id = parent_map[node_id] path.append(node_id) return path[::-1] # Инвертируем, чтобы получить путь от корня # Получаем путь от корня до целевого узла path_to_root = get_path_to_root(target_node_id) # Собираем сообщения, соответствующие узлам в пути messages_for_path = [] node_ids_in_path = set(path_to_root) for message in messages: if "node_id" in message and message["node_id"] in node_ids_in_path: messages_for_path.append({ "role": message["role"], "content": message["content"] }) return messages_for_path def delete_graph(self, graph_id: str) -> bool: """Удаляет граф из базы данных.""" conn = self._get_connection() cursor = conn.cursor() try: cursor.execute("DELETE FROM graphs WHERE id = ?", (graph_id, )) conn.commit() return True except Exception as e: print(f"Ошибка при удалении графа: {e}") return False finally: conn.close()