split graph_history_manager and title_generator
This commit is contained in:
parent
5a1fb4d482
commit
cb195fddc6
11
app/api.py
11
app/api.py
|
|
@ -8,17 +8,12 @@ from flask import Flask, request, jsonify
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
from workflows import run_agent, graph_history_manager
|
from workflows import run_agent, graph_history_manager
|
||||||
from llm_client import MODELS # Добавляем импорт списка моделей
|
from llm_client import MODELS # Добавляем импорт списка моделей
|
||||||
from title_generator import TitleGenerator
|
|
||||||
|
|
||||||
api = Flask(__name__)
|
api = Flask(__name__)
|
||||||
CORS(
|
CORS(
|
||||||
api
|
api
|
||||||
) # Разрешаем CORS для всех доменов (в production нужно настроить более строго)
|
) # Разрешаем CORS для всех доменов (в production нужно настроить более строго)
|
||||||
|
|
||||||
# Инициализируем сервис генерации заголовков
|
|
||||||
title_generator = TitleGenerator(graph_history_manager)
|
|
||||||
title_generator.start()
|
|
||||||
|
|
||||||
|
|
||||||
@api.route('/api/chat', methods=['POST'])
|
@api.route('/api/chat', methods=['POST'])
|
||||||
def chat():
|
def chat():
|
||||||
|
|
@ -90,9 +85,3 @@ def get_messages_from_root_to_node(graph_id, node_id):
|
||||||
def get_available_models():
|
def get_available_models():
|
||||||
"""API endpoint для получения списка доступных моделей."""
|
"""API endpoint для получения списка доступных моделей."""
|
||||||
return jsonify(list(MODELS.keys()))
|
return jsonify(list(MODELS.keys()))
|
||||||
|
|
||||||
@api.teardown_appcontext
|
|
||||||
def cleanup(error):
|
|
||||||
"""Очистка ресурсов при завершении приложения."""
|
|
||||||
if hasattr(api, '_title_generator'):
|
|
||||||
title_generator.stop()
|
|
||||||
|
|
@ -4,6 +4,7 @@ from typing import Dict, Any, List, Optional
|
||||||
import json
|
import json
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import uuid # Для генерации UUID
|
import uuid # Для генерации UUID
|
||||||
|
from title_generator import TitleGenerator
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------- Exceptions ------------------------------------------------------------
|
# ----------------------------------------------- Exceptions ------------------------------------------------------------
|
||||||
|
|
@ -24,6 +25,22 @@ class GraphHistoryManager:
|
||||||
self.db_path = db_path
|
self.db_path = db_path
|
||||||
self._create_tables()
|
self._create_tables()
|
||||||
|
|
||||||
|
# Инициализируем сервис генерации заголовков
|
||||||
|
self.title_generator = TitleGenerator(self)
|
||||||
|
self.title_generator.start()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""
|
||||||
|
Деструктор класса. Останавливает генератор заголовков при уничтожении объекта.
|
||||||
|
"""
|
||||||
|
print("🚩 Завершение работы GraphHistoryManager. Останавливаем генератор заголовков...")
|
||||||
|
self.stop_title_generator()
|
||||||
|
|
||||||
|
def stop_title_generator(self):
|
||||||
|
"""Останавливает процесс генерации заголовков."""
|
||||||
|
if self.title_generator:
|
||||||
|
self.title_generator.stop()
|
||||||
|
|
||||||
# ----------------------------------------------- Internal Methods ------------------------------------------------------------
|
# ----------------------------------------------- Internal Methods ------------------------------------------------------------
|
||||||
def _get_connection(self):
|
def _get_connection(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -70,17 +87,6 @@ class GraphHistoryManager:
|
||||||
FOREIGN KEY (target_node_id) REFERENCES graph_nodes_data (node_id) ON DELETE CASCADE
|
FOREIGN KEY (target_node_id) REFERENCES graph_nodes_data (node_id) ON DELETE CASCADE
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
# Очередь для обработки заголовков
|
|
||||||
cursor.execute("""
|
|
||||||
CREATE TABLE IF NOT EXISTS title_generation_queue (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
item_type TEXT NOT NULL, -- 'graph' or 'node'
|
|
||||||
graph_id TEXT NOT NULL,
|
|
||||||
node_id TEXT DEFAULT NULL, -- только для узлов
|
|
||||||
priority INTEGER DEFAULT 0, -- для приоритета обработки
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
# _get_max_graph_id - удален, так как используем UUID для graph_id
|
# _get_max_graph_id - удален, так как используем UUID для graph_id
|
||||||
|
|
@ -174,10 +180,7 @@ class GraphHistoryManager:
|
||||||
# Добавляем новые узлы
|
# Добавляем новые узлы
|
||||||
for node in new_nodes:
|
for node in new_nodes:
|
||||||
self._add_graph_node(conn, graph_id, node)
|
self._add_graph_node(conn, graph_id, node)
|
||||||
# Добавляем в очередь заголовков с высоким приоритетом
|
self.title_generator.add_node_to_queue(cursor, graph_id, node["id"])
|
||||||
cursor.execute(
|
|
||||||
"INSERT INTO title_generation_queue (item_type, graph_id, node_id, priority) VALUES (?, ?, ?, ?)",
|
|
||||||
("node", graph_id, node["id"], 10))
|
|
||||||
|
|
||||||
# Добавляем новые ребра
|
# Добавляем новые ребра
|
||||||
for edge in new_edges:
|
for edge in new_edges:
|
||||||
|
|
@ -189,9 +192,7 @@ class GraphHistoryManager:
|
||||||
|
|
||||||
# Если новый граф, добавляем его в очередь заголовков
|
# Если новый граф, добавляем его в очередь заголовков
|
||||||
if is_new_graph:
|
if is_new_graph:
|
||||||
cursor.execute(
|
self.title_generator.add_graph_to_queue(cursor, graph_id)
|
||||||
"INSERT INTO title_generation_queue (item_type, graph_id, priority) VALUES (?, ?, ?)",
|
|
||||||
("graph", graph_id, 10))
|
|
||||||
|
|
||||||
conn.commit() # Фиксируем все изменения
|
conn.commit() # Фиксируем все изменения
|
||||||
print(f"Изменения графа {graph_id} сохранены.")
|
print(f"Изменения графа {graph_id} сохранены.")
|
||||||
|
|
@ -356,74 +357,6 @@ class GraphHistoryManager:
|
||||||
print(f"Ошибка при удалении графа: {e}")
|
print(f"Ошибка при удалении графа: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# ----------------------------------------------- Title Generation Queue Methods ------------------------------------------------------------
|
|
||||||
|
|
||||||
def add_to_title_queue(self,
|
|
||||||
item_type: str,
|
|
||||||
graph_id: str,
|
|
||||||
node_id: str = None,
|
|
||||||
priority: int = 0):
|
|
||||||
"""Добавляет элемент в очередь генерации заголовков."""
|
|
||||||
with self._get_connection() as conn:
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute(
|
|
||||||
"INSERT INTO title_generation_queue (item_type, graph_id, node_id, priority) VALUES (?, ?, ?, ?)",
|
|
||||||
(item_type, graph_id, node_id, priority))
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
def get_next_from_title_queue(self) -> Optional[Dict[str, Any]]:
|
|
||||||
"""Получает следующий элемент из очереди для обработки."""
|
|
||||||
with self._get_connection() as conn:
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("""
|
|
||||||
SELECT id, item_type, graph_id, node_id
|
|
||||||
FROM title_generation_queue
|
|
||||||
ORDER BY priority DESC, id ASC
|
|
||||||
LIMIT 1
|
|
||||||
""")
|
|
||||||
result = cursor.fetchone()
|
|
||||||
if result:
|
|
||||||
queue_id, item_type, graph_id, node_id = result
|
|
||||||
# Удаляем из очереди
|
|
||||||
cursor.execute(
|
|
||||||
"DELETE FROM title_generation_queue WHERE id = ?",
|
|
||||||
(queue_id, ))
|
|
||||||
conn.commit()
|
|
||||||
return {
|
|
||||||
"item_type": item_type,
|
|
||||||
"graph_id": graph_id,
|
|
||||||
"node_id": node_id
|
|
||||||
}
|
|
||||||
return None
|
|
||||||
|
|
||||||
def populate_initial_title_queue(self):
|
|
||||||
"""Заполняет очередь элементами без сгенерированных заголовков при старте сервера."""
|
|
||||||
with self._get_connection() as conn:
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Очищаем существующую очередь
|
|
||||||
cursor.execute("DELETE FROM title_generation_queue")
|
|
||||||
|
|
||||||
# Добавляем графы без заголовков
|
|
||||||
cursor.execute(
|
|
||||||
"SELECT id FROM graphs WHERE title_generated = FALSE OR title_generated IS NULL"
|
|
||||||
)
|
|
||||||
for (graph_id, ) in cursor.fetchall():
|
|
||||||
cursor.execute(
|
|
||||||
"INSERT INTO title_generation_queue (item_type, graph_id, priority) VALUES (?, ?, ?)",
|
|
||||||
("graph", graph_id, 1))
|
|
||||||
|
|
||||||
# Добавляем узлы без заголовков
|
|
||||||
cursor.execute(
|
|
||||||
"SELECT graph_id, node_id FROM graph_nodes_data WHERE title_generated = FALSE OR title_generated IS NULL"
|
|
||||||
)
|
|
||||||
for graph_id, node_id in cursor.fetchall():
|
|
||||||
cursor.execute(
|
|
||||||
"INSERT INTO title_generation_queue (item_type, graph_id, node_id, priority) VALUES (?, ?, ?, ?)",
|
|
||||||
("node", graph_id, node_id, 1))
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
# ----------------------------------------------- Title Update Methods ------------------------------------------------------------
|
# ----------------------------------------------- Title Update Methods ------------------------------------------------------------
|
||||||
|
|
||||||
def update_graph_title(self, graph_id: str, title: str):
|
def update_graph_title(self, graph_id: str, title: str):
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
Работает в фоновом режиме, обрабатывая очередь элементов без заголовков.
|
Работает в фоновом режиме, обрабатывая очередь элементов без заголовков.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from typing import Optional
|
from typing import Any, Dict, Optional
|
||||||
from graph_history_manager import GraphHistoryManager
|
|
||||||
from nodes import DEFAULT_SUMMARIZATION_LLM_NAME
|
from nodes import DEFAULT_SUMMARIZATION_LLM_NAME
|
||||||
from llm_client import get_llm
|
from llm_client import get_llm
|
||||||
from langchain_core.messages import SystemMessage, HumanMessage
|
from langchain_core.messages import SystemMessage, HumanMessage
|
||||||
|
|
@ -14,7 +14,9 @@ from langchain_core.messages import SystemMessage, HumanMessage
|
||||||
class TitleGenerator:
|
class TitleGenerator:
|
||||||
"""Сервис для асинхронной генерации заголовков."""
|
"""Сервис для асинхронной генерации заголовков."""
|
||||||
|
|
||||||
def __init__(self, history_manager: GraphHistoryManager):
|
def __init__(self, history_manager, db_path="graph_history.db"):
|
||||||
|
self.db_path = db_path
|
||||||
|
self._create_table()
|
||||||
self.history_manager = history_manager
|
self.history_manager = history_manager
|
||||||
self.llm = get_llm(DEFAULT_SUMMARIZATION_LLM_NAME)
|
self.llm = get_llm(DEFAULT_SUMMARIZATION_LLM_NAME)
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
@ -27,6 +29,31 @@ class TitleGenerator:
|
||||||
self.node_title_prompt = """Создай краткий заголовок (максимум 40 символов) для этого сообщения/действия.
|
self.node_title_prompt = """Создай краткий заголовок (максимум 40 символов) для этого сообщения/действия.
|
||||||
Заголовок должен кратко описывать суть сообщения или действия. Отвечай только заголовком, без дополнительных объяснений. Ты должен сделать саммери, а не ответить на вопросы, если они есть в сообщении."""
|
Заголовок должен кратко описывать суть сообщения или действия. Отвечай только заголовком, без дополнительных объяснений. Ты должен сделать саммери, а не ответить на вопросы, если они есть в сообщении."""
|
||||||
|
|
||||||
|
def _get_connection(self):
|
||||||
|
"""
|
||||||
|
Получает соединение с базой данных.
|
||||||
|
Устанавливаем таймаут для ожидания блокировки базы данных при конкурентной записи.
|
||||||
|
"""
|
||||||
|
return sqlite3.connect(self.db_path,
|
||||||
|
timeout=5.0) # Увеличил таймаут до 5 секунд
|
||||||
|
|
||||||
|
def _create_table(self):
|
||||||
|
"""Создает таблицы для хранения графов, если они не существуют."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
# Очередь для обработки заголовков
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS title_generation_queue (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
item_type TEXT NOT NULL, -- 'graph' or 'node'
|
||||||
|
graph_id TEXT NOT NULL,
|
||||||
|
node_id TEXT DEFAULT NULL, -- только для узлов
|
||||||
|
priority INTEGER DEFAULT 0, -- для приоритета обработки
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Запускает фоновый процесс генерации заголовков."""
|
"""Запускает фоновый процесс генерации заголовков."""
|
||||||
if self.running:
|
if self.running:
|
||||||
|
|
@ -35,13 +62,41 @@ class TitleGenerator:
|
||||||
self.running = True
|
self.running = True
|
||||||
|
|
||||||
# Заполняем очередь при старте
|
# Заполняем очередь при старте
|
||||||
self.history_manager.populate_initial_title_queue()
|
self.populate_initial_title_queue()
|
||||||
|
|
||||||
# Запускаем фоновый поток
|
# Запускаем фоновый поток
|
||||||
self.thread = threading.Thread(target=self._process_queue, daemon=True)
|
self.thread = threading.Thread(target=self._process_queue, daemon=True)
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
print("Сервис генерации заголовков запущен")
|
print("Сервис генерации заголовков запущен")
|
||||||
|
|
||||||
|
def populate_initial_title_queue(self):
|
||||||
|
"""Заполняет очередь элементами без сгенерированных заголовков при старте сервера."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Очищаем существующую очередь
|
||||||
|
cursor.execute("DELETE FROM title_generation_queue")
|
||||||
|
|
||||||
|
# Добавляем графы без заголовков
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT id FROM graphs WHERE title_generated = FALSE OR title_generated IS NULL"
|
||||||
|
)
|
||||||
|
for (graph_id, ) in cursor.fetchall():
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO title_generation_queue (item_type, graph_id, priority) VALUES (?, ?, ?)",
|
||||||
|
("graph", graph_id, 1))
|
||||||
|
|
||||||
|
# Добавляем узлы без заголовков
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT graph_id, node_id FROM graph_nodes_data WHERE title_generated = FALSE OR title_generated IS NULL"
|
||||||
|
)
|
||||||
|
for graph_id, node_id in cursor.fetchall():
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO title_generation_queue (item_type, graph_id, node_id, priority) VALUES (?, ?, ?, ?)",
|
||||||
|
("node", graph_id, node_id, 1))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""Останавливает фоновый процесс."""
|
"""Останавливает фоновый процесс."""
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
@ -53,7 +108,7 @@ class TitleGenerator:
|
||||||
"""Основной цикл обработки очереди заголовков."""
|
"""Основной цикл обработки очереди заголовков."""
|
||||||
while self.running:
|
while self.running:
|
||||||
try:
|
try:
|
||||||
item = self.history_manager.get_next_from_title_queue()
|
item = self.get_next_from_title_queue()
|
||||||
|
|
||||||
if item:
|
if item:
|
||||||
if item["item_type"] == "graph":
|
if item["item_type"] == "graph":
|
||||||
|
|
@ -68,6 +123,31 @@ class TitleGenerator:
|
||||||
print(f"Ошибка при обработке очереди заголовков: {e}")
|
print(f"Ошибка при обработке очереди заголовков: {e}")
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
|
||||||
|
def get_next_from_title_queue(self) -> Optional[Dict[str, Any]]:
|
||||||
|
"""Получает следующий элемент из очереди для обработки."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT id, item_type, graph_id, node_id
|
||||||
|
FROM title_generation_queue
|
||||||
|
ORDER BY priority DESC, id ASC
|
||||||
|
LIMIT 1
|
||||||
|
""")
|
||||||
|
result = cursor.fetchone()
|
||||||
|
if result:
|
||||||
|
queue_id, item_type, graph_id, node_id = result
|
||||||
|
# Удаляем из очереди
|
||||||
|
cursor.execute(
|
||||||
|
"DELETE FROM title_generation_queue WHERE id = ?",
|
||||||
|
(queue_id, ))
|
||||||
|
conn.commit()
|
||||||
|
return {
|
||||||
|
"item_type": item_type,
|
||||||
|
"graph_id": graph_id,
|
||||||
|
"node_id": node_id
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
def _generate_graph_title(self, graph_id: str):
|
def _generate_graph_title(self, graph_id: str):
|
||||||
"""Генерирует заголовок для графа."""
|
"""Генерирует заголовок для графа."""
|
||||||
try:
|
try:
|
||||||
|
|
@ -149,3 +229,27 @@ class TitleGenerator:
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Ошибка генерации заголовка узла {node_id}: {e}")
|
print(f"Ошибка генерации заголовка узла {node_id}: {e}")
|
||||||
|
|
||||||
|
'''def add_to_title_queue(self,
|
||||||
|
item_type: str,
|
||||||
|
graph_id: str,
|
||||||
|
node_id: str = None,
|
||||||
|
priority: int = 0):
|
||||||
|
"""Добавляет элемент в очередь генерации заголовков."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO title_generation_queue (item_type, graph_id, node_id, priority) VALUES (?, ?, ?, ?)",
|
||||||
|
(item_type, graph_id, node_id, priority))
|
||||||
|
conn.commit()'''
|
||||||
|
|
||||||
|
def add_node_to_queue(self, cursor, graph_id, node_id):
|
||||||
|
# Добавляем в очередь заголовков с высоким приоритетом
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO title_generation_queue (item_type, graph_id, node_id, priority) VALUES (?, ?, ?, ?)",
|
||||||
|
("node", graph_id, node_id, 10))
|
||||||
|
|
||||||
|
def add_graph_to_queue(self, cursor, graph_id):
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO title_generation_queue (item_type, graph_id, priority) VALUES (?, ?, ?)",
|
||||||
|
("graph", graph_id, 10))
|
||||||
|
|
|
||||||
BIN
graph_history.db
BIN
graph_history.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user