Fix bug with sending all the messages from all nodes instead of current branch
This commit is contained in:
parent
b634337dda
commit
c1d990fd93
|
|
@ -17,6 +17,7 @@ class GraphHistoryManager:
|
||||||
|
|
||||||
def __init__(self, db_path="graph_history.db"):
|
def __init__(self, db_path="graph_history.db"):
|
||||||
self.db_path = db_path
|
self.db_path = db_path
|
||||||
|
self._create_table(self._get_connection()) # Создаем таблицу при инициализации
|
||||||
|
|
||||||
def _get_connection(self):
|
def _get_connection(self):
|
||||||
"""Получает соединение с базой данных."""
|
"""Получает соединение с базой данных."""
|
||||||
|
|
@ -28,7 +29,6 @@ class GraphHistoryManager:
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS graphs (
|
CREATE TABLE IF NOT EXISTS graphs (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
messages TEXT,
|
|
||||||
graph_nodes TEXT,
|
graph_nodes TEXT,
|
||||||
graph_edges TEXT,
|
graph_edges TEXT,
|
||||||
current_node_id TEXT
|
current_node_id TEXT
|
||||||
|
|
@ -45,8 +45,12 @@ class GraphHistoryManager:
|
||||||
result = cursor.fetchone()[0]
|
result = cursor.fetchone()[0]
|
||||||
return result if result is not None else 0
|
return result if result is not None else 0
|
||||||
|
|
||||||
|
# ---------------------------------------------------- public methods ----------------------------------------------------------------
|
||||||
def save_graph(self, graph_data: Any) -> str:
|
def save_graph(self, graph_data: Any) -> str:
|
||||||
"""Сохраняет или обновляет данные графа в базе данных и возвращает ID."""
|
"""
|
||||||
|
Сохраняет или обновляет данные графа в базе данных и возвращает ID.
|
||||||
|
Не сохраняет полную историю сообщений, только структуру графа.
|
||||||
|
"""
|
||||||
conn = self._get_connection()
|
conn = self._get_connection()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
try:
|
try:
|
||||||
|
|
@ -58,7 +62,6 @@ class GraphHistoryManager:
|
||||||
graph_data["id"] = graph_id
|
graph_data["id"] = graph_id
|
||||||
|
|
||||||
# Преобразуем структуры данных в JSON-строки для хранения в SQLite
|
# Преобразуем структуры данных в JSON-строки для хранения в SQLite
|
||||||
messages_json = json.dumps(graph_data.get("messages", []))
|
|
||||||
graph_nodes_json = json.dumps(graph_data.get("graph_nodes", []))
|
graph_nodes_json = json.dumps(graph_data.get("graph_nodes", []))
|
||||||
graph_edges_json = json.dumps(graph_data.get("graph_edges", []))
|
graph_edges_json = json.dumps(graph_data.get("graph_edges", []))
|
||||||
current_node_id = graph_data.get("current_node_id", "")
|
current_node_id = graph_data.get("current_node_id", "")
|
||||||
|
|
@ -72,20 +75,19 @@ class GraphHistoryManager:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
UPDATE graphs SET
|
UPDATE graphs SET
|
||||||
messages = ?,
|
|
||||||
graph_nodes = ?,
|
graph_nodes = ?,
|
||||||
graph_edges = ?,
|
graph_edges = ?,
|
||||||
current_node_id = ?
|
current_node_id = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
""", (messages_json, graph_nodes_json, graph_edges_json,
|
""", (graph_nodes_json, graph_edges_json,
|
||||||
current_node_id, graph_id))
|
current_node_id, graph_id))
|
||||||
else:
|
else:
|
||||||
# Вставляем новую запись
|
# Вставляем новую запись
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO graphs (id, messages, graph_nodes, graph_edges, current_node_id)
|
INSERT INTO graphs (id, graph_nodes, graph_edges, current_node_id)
|
||||||
VALUES (?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?)
|
||||||
""", (graph_id, messages_json, graph_nodes_json,
|
""", (graph_id, graph_nodes_json,
|
||||||
graph_edges_json, current_node_id))
|
graph_edges_json, current_node_id))
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
@ -94,29 +96,51 @@ class GraphHistoryManager:
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def get_graph(self, graph_id: str) -> Optional[Any]:
|
def get_graph(self, graph_id: str, target_node_id: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
||||||
"""Получает данные графа по ID из базы данных."""
|
"""
|
||||||
|
Получает данные графа по ID из базы данных.
|
||||||
|
Динамически вычисляет 'messages' до 'target_node_id'.
|
||||||
|
Если target_node_id не указан, используется current_node_id из БД.
|
||||||
|
"""
|
||||||
conn = self._get_connection()
|
conn = self._get_connection()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
try:
|
try:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"SELECT messages, graph_nodes, graph_edges, current_node_id FROM graphs WHERE id = ?",
|
"SELECT graph_nodes, graph_edges, current_node_id FROM graphs WHERE id = ?",
|
||||||
(graph_id, ))
|
(graph_id, ))
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
messages_json, graph_nodes_json, graph_edges_json, current_node_id = result
|
graph_nodes_json, graph_edges_json, db_current_node_id = result
|
||||||
|
|
||||||
# Преобразуем JSON-строки обратно в структуры данных Python
|
# Преобразуем JSON-строки обратно в структуры данных Python
|
||||||
messages = json.loads(messages_json)
|
|
||||||
graph_nodes = json.loads(graph_nodes_json)
|
graph_nodes = json.loads(graph_nodes_json)
|
||||||
graph_edges = json.loads(graph_edges_json)
|
graph_edges = json.loads(graph_edges_json)
|
||||||
|
|
||||||
|
resolved_current_node_id = target_node_id if target_node_id else db_current_node_id
|
||||||
|
|
||||||
|
# Если нет узлов, значит граф пуст или некорректен
|
||||||
|
if not graph_nodes:
|
||||||
|
return {
|
||||||
|
"id": graph_id,
|
||||||
|
"messages": [],
|
||||||
|
"graph_nodes": [],
|
||||||
|
"graph_edges": [],
|
||||||
|
"current_node_id": resolved_current_node_id
|
||||||
|
}
|
||||||
|
|
||||||
|
# Динамически собираем сообщения
|
||||||
|
messages = self.get_messages_from_root_to_node(
|
||||||
|
{"graph_nodes": graph_nodes, "graph_edges": graph_edges},
|
||||||
|
resolved_current_node_id
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": graph_id,
|
"id": graph_id,
|
||||||
"messages": messages,
|
"messages": messages, # Здесь будут вычисленные сообщения
|
||||||
"graph_nodes": graph_nodes,
|
"graph_nodes": graph_nodes,
|
||||||
"graph_edges": graph_edges,
|
"graph_edges": graph_edges,
|
||||||
"current_node_id": current_node_id
|
"current_node_id": resolved_current_node_id
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
@ -124,54 +148,80 @@ class GraphHistoryManager:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def get_all_graphs_summary(self) -> List[Dict[str, str]]:
|
def get_all_graphs_summary(self) -> List[Dict[str, str]]:
|
||||||
"""Возвращает краткий список всех сохраненных графов."""
|
"""
|
||||||
|
Возвращает краткий список всех сохраненных графов.
|
||||||
|
Первое сообщение извлекается путем построения пути к current_node_id
|
||||||
|
и взятия первого сообщения.
|
||||||
|
"""
|
||||||
conn = self._get_connection()
|
conn = self._get_connection()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
try:
|
try:
|
||||||
cursor.execute("SELECT id, messages FROM graphs")
|
cursor.execute("SELECT id, graph_nodes, graph_edges, current_node_id FROM graphs")
|
||||||
graphs_data = cursor.fetchall()
|
graphs_data = cursor.fetchall()
|
||||||
return [{
|
summaries = []
|
||||||
"id":
|
for gid, nodes_json, edges_json, current_node_id in graphs_data:
|
||||||
gid,
|
graph_nodes = json.loads(nodes_json)
|
||||||
"first_message":
|
graph_edges = json.loads(edges_json)
|
||||||
json.loads(messages)[0].get("content", "No content")
|
|
||||||
if messages else "No content"
|
# Получаем все сообщения до current_node_id
|
||||||
} for gid, messages in graphs_data]
|
full_messages = self.get_messages_from_root_to_node(
|
||||||
|
{"graph_nodes": graph_nodes, "graph_edges": graph_edges},
|
||||||
|
current_node_id
|
||||||
|
)
|
||||||
|
|
||||||
|
first_message_content = "No content"
|
||||||
|
if full_messages:
|
||||||
|
# Ищем первое сообщение, которое является пользовательским
|
||||||
|
for msg in full_messages:
|
||||||
|
if msg.get("role") == "user":
|
||||||
|
first_message_content = msg.get("content", "No content")
|
||||||
|
break
|
||||||
|
if first_message_content == "No content": # Если не нашли пользовательского, берем первое ассистента
|
||||||
|
first_message_content = full_messages[0].get("content", "No content")
|
||||||
|
|
||||||
|
summaries.append({
|
||||||
|
"id": gid,
|
||||||
|
"first_message": first_message_content
|
||||||
|
})
|
||||||
|
return summaries
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def get_messages_from_root_to_node(
|
def get_messages_from_root_to_node(
|
||||||
self, graph_data: Dict[str, Any],
|
self, graph_data: Dict[str, Any],
|
||||||
target_node_id: str) -> List[Dict[str, str]]:
|
target_node_id: str) -> List[Dict[str, str]]:
|
||||||
"""Получает список сообщений от корневого узла до указанного узла."""
|
"""
|
||||||
nodes = graph_data.get("graph_nodes", [])
|
Получает список сообщений от корневого узла до указанного узла.
|
||||||
|
Сообщения извлекаются из данных узлов, а не из отдельного поля.
|
||||||
|
"""
|
||||||
|
all_nodes = graph_data.get("graph_nodes", [])
|
||||||
edges = graph_data.get("graph_edges", [])
|
edges = graph_data.get("graph_edges", [])
|
||||||
messages = graph_data.get("messages", [])
|
|
||||||
|
|
||||||
|
# Создаем словарь для быстрого поиска узлов по ID
|
||||||
|
node_map = {node['id']: node for node in all_nodes}
|
||||||
# Создаем словарь для быстрого поиска родительских узлов
|
# Создаем словарь для быстрого поиска родительских узлов
|
||||||
parent_map = {edge['target']: edge['source'] for edge in edges}
|
parent_map = {edge['target']: edge['source'] for edge in edges}
|
||||||
|
|
||||||
# Функция для рекурсивного подъема по дереву до корневого узла
|
# Функция для рекурсивного подъема по дереву до корневого узла
|
||||||
def get_path_to_root(node_id: str) -> List[str]:
|
def get_path_to_root(node_id: str) -> List[str]:
|
||||||
path = [node_id]
|
path = [node_id]
|
||||||
while node_id in parent_map:
|
current = node_id
|
||||||
node_id = parent_map[node_id]
|
while current in parent_map:
|
||||||
path.append(node_id)
|
current = parent_map[current]
|
||||||
|
path.append(current)
|
||||||
return path[::-1] # Инвертируем, чтобы получить путь от корня
|
return path[::-1] # Инвертируем, чтобы получить путь от корня
|
||||||
|
|
||||||
# Получаем путь от корня до целевого узла
|
# Получаем путь от корня до целевого узла
|
||||||
path_to_root = get_path_to_root(target_node_id)
|
path_to_target = get_path_to_root(target_node_id)
|
||||||
|
|
||||||
# Собираем сообщения, соответствующие узлам в пути
|
|
||||||
|
# Собираем сообщения, соответствующие узлам в пути.
|
||||||
|
# Сообщения теперь хранятся в 'data' каждого узла при создании.
|
||||||
messages_for_path = []
|
messages_for_path = []
|
||||||
node_ids_in_path = set(path_to_root)
|
for node_id in path_to_target:
|
||||||
|
node = node_map.get(node_id)
|
||||||
for message in messages:
|
if node and 'message' in node.get('data', {}): # Проверяем наличие поля 'message'
|
||||||
if "node_id" in message and message["node_id"] in node_ids_in_path:
|
messages_for_path.append(node['data']['message'])
|
||||||
messages_for_path.append({
|
|
||||||
"role": message["role"],
|
|
||||||
"content": message["content"]
|
|
||||||
})
|
|
||||||
|
|
||||||
return messages_for_path
|
return messages_for_path
|
||||||
|
|
||||||
|
|
@ -182,7 +232,7 @@ class GraphHistoryManager:
|
||||||
try:
|
try:
|
||||||
cursor.execute("DELETE FROM graphs WHERE id = ?", (graph_id, ))
|
cursor.execute("DELETE FROM graphs WHERE id = ?", (graph_id, ))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return True
|
return cursor.rowcount > 0 # Возвращает True, если была удалена хотя бы одна строка
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Ошибка при удалении графа: {e}")
|
print(f"Ошибка при удалении графа: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from typing import Dict, Any, List
|
from typing import Dict, Any, List
|
||||||
from langchain_core.messages import HumanMessage, SystemMessage
|
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
|
||||||
|
|
||||||
# --- Конфигурация LLM ---
|
# --- Конфигурация LLM ---
|
||||||
# Пустой словарь для локальных моделей (Ollama)
|
# Пустой словарь для локальных моделей (Ollama)
|
||||||
|
|
@ -101,6 +101,11 @@ class CustomLLM:
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": msg.content
|
"content": msg.content
|
||||||
})
|
})
|
||||||
|
elif isinstance(msg, AIMessage): # Добавляем обработку AIMessage
|
||||||
|
openai_messages.append({
|
||||||
|
"role": "assistant",
|
||||||
|
"content": msg.content
|
||||||
|
})
|
||||||
else: # Предполагаем, что это другие типы сообщений Langchain или словари
|
else: # Предполагаем, что это другие типы сообщений Langchain или словари
|
||||||
openai_messages.append({
|
openai_messages.append({
|
||||||
"role":
|
"role":
|
||||||
|
|
@ -155,6 +160,11 @@ class CustomLLM:
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": msg.content
|
"content": msg.content
|
||||||
})
|
})
|
||||||
|
elif isinstance(msg, AIMessage): # Добавляем обработку AIMessage
|
||||||
|
mistral_messages.append({
|
||||||
|
"role": "assistant",
|
||||||
|
"content": msg.content
|
||||||
|
})
|
||||||
else: # Предполагаем, что это другие типы сообщений Langchain или словари
|
else: # Предполагаем, что это другие типы сообщений Langchain или словари
|
||||||
mistral_messages.append({
|
mistral_messages.append({
|
||||||
"role":
|
"role":
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,6 @@ class AgentState(BaseModel):
|
||||||
command: Optional[str] = Field(None,
|
command: Optional[str] = Field(None,
|
||||||
description="Распознанная слеш-команда.")
|
description="Распознанная слеш-команда.")
|
||||||
command_args: List[str] = Field([], description="Аргументы команды.")
|
command_args: List[str] = Field([], description="Аргументы команды.")
|
||||||
chat_history: List[Dict[str, str]] = Field(
|
|
||||||
[],
|
|
||||||
description=
|
|
||||||
"Полная история сообщений в текущей ветке диалога. Формат: [{'role': 'user/assistant', 'content': 'message'}]"
|
|
||||||
)
|
|
||||||
llm_response: Optional[str] = Field(None, description="Ответ от LLM.")
|
llm_response: Optional[str] = Field(None, description="Ответ от LLM.")
|
||||||
image_urls: List[str] = Field(
|
image_urls: List[str] = Field(
|
||||||
[], description="URL сгенерированных изображений.")
|
[], description="URL сгенерированных изображений.")
|
||||||
|
|
@ -37,3 +32,7 @@ class AgentState(BaseModel):
|
||||||
"start", description="ID текущего узла графа для фронтенда.")
|
"start", description="ID текущего узла графа для фронтенда.")
|
||||||
parent_node_id: Optional[str] = Field(None,
|
parent_node_id: Optional[str] = Field(None,
|
||||||
description="ID родительского узла.")
|
description="ID родительского узла.")
|
||||||
|
# Временное хранилище для chat_history, которое не сохраняется в БД
|
||||||
|
# Оно используется для передачи контекста между узлами в пределах одного выполнения
|
||||||
|
temporary_chat_history: List[Dict[str, Any]] = Field(
|
||||||
|
[], description="Временная история чата, не сохраняемая в БД.")
|
||||||
|
|
|
||||||
220
app/nodes.py
220
app/nodes.py
|
|
@ -4,7 +4,7 @@
|
||||||
разбор команд, вызов LLM или выполнение сервисов.
|
разбор команд, вызов LLM или выполнение сервисов.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from langchain_core.messages import HumanMessage, SystemMessage
|
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
|
||||||
|
|
||||||
from llm_client import get_llm
|
from llm_client import get_llm
|
||||||
from services import ImageGenerationService, SubtitleService, ImageAnalysisService, SummarizationService
|
from services import ImageGenerationService, SubtitleService, ImageAnalysisService, SummarizationService
|
||||||
|
|
@ -66,21 +66,28 @@ class CommandManager:
|
||||||
command_manager = CommandManager()
|
command_manager = CommandManager()
|
||||||
|
|
||||||
|
|
||||||
|
# Здесь мы меняем состояние графа, добавляя сообщение от пользователя
|
||||||
def parse_command_node(state: AgentState) -> AgentState:
|
def parse_command_node(state: AgentState) -> AgentState:
|
||||||
"""Парсит вход пользователя на предмет слеш-команды."""
|
"""
|
||||||
|
Парсит вход пользователя на предмет слеш-команды и инициализирует узел пользователя в графе.
|
||||||
|
"""
|
||||||
print("Выполняется узел: parse_command_node")
|
print("Выполняется узел: parse_command_node")
|
||||||
user_input = state.input.strip()
|
user_input = state.input.strip()
|
||||||
|
|
||||||
# Добавляем узел пользователя в граф для фронтенда
|
# Создаем ID для узла пользователя
|
||||||
user_node_id = f"user_{len(state.graph_nodes) + 1}"
|
user_node_id = f"user_{len(state.graph_nodes) + 1}"
|
||||||
|
|
||||||
|
# Добавляем узел пользователя в граф для фронтенда с полным текстом сообщения
|
||||||
state.graph_nodes.append({
|
state.graph_nodes.append({
|
||||||
"id": user_node_id,
|
"id": user_node_id,
|
||||||
"type": "user",
|
"type": "user",
|
||||||
"data": {
|
"data": {
|
||||||
"label":
|
"label": user_input[:30] + "..." if len(user_input) > 30 else user_input,
|
||||||
user_input[:30] + "..." if len(user_input) > 30 else user_input
|
"message": {"role": "user", "content": user_input, "node_id": user_node_id} # Сохраняем сообщение здесь
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Добавляем ребро к новому узлу, если есть родительский узел
|
||||||
if state.parent_node_id:
|
if state.parent_node_id:
|
||||||
state.graph_edges.append({
|
state.graph_edges.append({
|
||||||
"id": f"e{state.parent_node_id}-{user_node_id}",
|
"id": f"e{state.parent_node_id}-{user_node_id}",
|
||||||
|
|
@ -90,10 +97,12 @@ def parse_command_node(state: AgentState) -> AgentState:
|
||||||
state.parent_node_id = user_node_id
|
state.parent_node_id = user_node_id
|
||||||
state.current_node_id = user_node_id
|
state.current_node_id = user_node_id
|
||||||
|
|
||||||
state.chat_history.append({
|
# Обновляем временную историю чата, которая используется для передачи между узлами
|
||||||
|
# и для вызова LLM в текущем раунде.
|
||||||
|
state.temporary_chat_history.append({
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": user_input,
|
"content": user_input,
|
||||||
"node_id": user_node_id # Сохраняем ID узла
|
"node_id": user_node_id
|
||||||
})
|
})
|
||||||
|
|
||||||
if user_input.startswith('/'):
|
if user_input.startswith('/'):
|
||||||
|
|
@ -116,7 +125,10 @@ def parse_command_node(state: AgentState) -> AgentState:
|
||||||
|
|
||||||
|
|
||||||
def execute_command_node(state: AgentState) -> AgentState:
|
def execute_command_node(state: AgentState) -> AgentState:
|
||||||
"""Выполняет соответствующий workflow для команды."""
|
"""
|
||||||
|
Выполняет соответствующий workflow для команды.
|
||||||
|
Этот узел служит маршрутизатором для выбора следующего узла в зависимости от команды.
|
||||||
|
"""
|
||||||
print(
|
print(
|
||||||
f"Выполняется узел: execute_command_node для команды: {state.command}")
|
f"Выполняется узел: execute_command_node для команды: {state.command}")
|
||||||
# Этот узел будет выступать в роли маршрутизатора
|
# Этот узел будет выступать в роли маршрутизатора
|
||||||
|
|
@ -131,27 +143,32 @@ main_llm = get_llm(DEFAULT_LLM_NAME)
|
||||||
|
|
||||||
|
|
||||||
def call_llm_node(state: AgentState) -> AgentState:
|
def call_llm_node(state: AgentState) -> AgentState:
|
||||||
"""Вызывает LLM для обработки обычного диалога или генерации ответа."""
|
"""
|
||||||
|
Вызывает LLM для обработки обычного диалога или генерации ответа.
|
||||||
|
Использует `temporary_chat_history` для передачи контекста LLM.
|
||||||
|
"""
|
||||||
print("Выполняется узел: call_llm_node (для обычного чата)")
|
print("Выполняется узел: call_llm_node (для обычного чата)")
|
||||||
try:
|
try:
|
||||||
# Для обычного чата LLM использует всю историю
|
# TODO: добавить в переписку ещё SystemMessage с системным промптом, типа: messages_for_llm.append(SystemMessage(content=msg["content"]))
|
||||||
|
# Для обычного чата LLM использует текущую временную историю
|
||||||
messages_for_llm = []
|
messages_for_llm = []
|
||||||
for msg in state.chat_history:
|
for msg in state.temporary_chat_history: # Используем temporary_chat_history
|
||||||
if msg["role"] == "user":
|
if msg["role"] == "user":
|
||||||
messages_for_llm.append(HumanMessage(content=msg["content"]))
|
messages_for_llm.append(HumanMessage(content=msg["content"]))
|
||||||
elif msg["role"] == "assistant":
|
elif msg["role"] == "assistant":
|
||||||
messages_for_llm.append(SystemMessage(content=msg["content"]))
|
messages_for_llm.append(AIMessage(content=msg["content"]))
|
||||||
|
|
||||||
response = main_llm.invoke(messages_for_llm)
|
response = main_llm.invoke(messages_for_llm)
|
||||||
|
|
||||||
# Добавляем узел LLM в граф для фронтенда
|
|
||||||
llm_node_id = f"llm_{len(state.graph_nodes) + 1}"
|
llm_node_id = f"llm_{len(state.graph_nodes) + 1}"
|
||||||
|
|
||||||
|
# Добавляем узел LLM в граф для фронтенда с полным текстом ответа
|
||||||
state.graph_nodes.append({
|
state.graph_nodes.append({
|
||||||
"id": llm_node_id,
|
"id": llm_node_id,
|
||||||
"type": "llm",
|
"type": "llm",
|
||||||
"data": {
|
"data": {
|
||||||
"label":
|
"label": response[:30] + "..." if len(response) > 30 else response,
|
||||||
response[:30] + "..." if len(response) > 30 else response
|
"message": {"role": "assistant", "content": response, "node_id": llm_node_id} # Сохраняем сообщение здесь
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.graph_edges.append({
|
state.graph_edges.append({
|
||||||
|
|
@ -162,10 +179,11 @@ def call_llm_node(state: AgentState) -> AgentState:
|
||||||
state.parent_node_id = llm_node_id
|
state.parent_node_id = llm_node_id
|
||||||
state.current_node_id = llm_node_id
|
state.current_node_id = llm_node_id
|
||||||
|
|
||||||
state.chat_history.append({
|
# Обновляем временную историю чата с ответом LLM
|
||||||
|
state.temporary_chat_history.append({
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"content": response,
|
"content": response,
|
||||||
"node_id": llm_node_id # Сохраняем ID узла
|
"node_id": llm_node_id
|
||||||
})
|
})
|
||||||
state.llm_response = response
|
state.llm_response = response
|
||||||
|
|
||||||
|
|
@ -182,12 +200,16 @@ image_gen_service = ImageGenerationService(
|
||||||
|
|
||||||
|
|
||||||
def generate_images_node(state: AgentState) -> AgentState:
|
def generate_images_node(state: AgentState) -> AgentState:
|
||||||
"""Генерирует изображения по промпту."""
|
"""
|
||||||
|
Генерирует изображения по промпту, переданному в команде `/imagine`.
|
||||||
|
"""
|
||||||
print("Выполняется узел: generate_images_node")
|
print("Выполняется узел: generate_images_node")
|
||||||
prompt = " ".join(state.command_args)
|
prompt = " ".join(state.command_args)
|
||||||
if not prompt:
|
if not prompt:
|
||||||
state.error = "Для команды /imagine требуется промпт. Пример: /imagine кошка на луне"
|
state.error = "Для команды /imagine требуется промпт. Пример: /imagine кошка на луне"
|
||||||
state.llm_response = state.error
|
state.llm_response = state.error
|
||||||
|
# Добавляем ошибку во временную историю для согласованности
|
||||||
|
state.temporary_chat_history.append({"role": "assistant", "content": state.error})
|
||||||
return state
|
return state
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -196,20 +218,21 @@ def generate_images_node(state: AgentState) -> AgentState:
|
||||||
|
|
||||||
# Формируем текст ответа с использованием Markdown для отображения изображений
|
# Формируем текст ответа с использованием Markdown для отображения изображений
|
||||||
image_markdown = "\n".join([
|
image_markdown = "\n".join([
|
||||||
f'<a href="{url}"><img src="{url}" alt="image{i}" style="max-width: 300px; max-height: 300px;"></a>'
|
f'<a href="{url}" target="_blank"><img src="{url}" alt="image{i}" style="max-width: 300px; max-height: 300px;"></a>'
|
||||||
for i, url in enumerate(urls)
|
for i, url in enumerate(urls)
|
||||||
])
|
])
|
||||||
response_text = f"Сгенерировано {len(urls)} изображений:\n{image_markdown}"
|
response_text = f"Сгенерировано {len(urls)} изображений:\n{image_markdown}"
|
||||||
state.llm_response = response_text
|
state.llm_response = response_text
|
||||||
|
|
||||||
# Добавляем узел генерации изображений в граф
|
|
||||||
img_gen_node_id = f"img_gen_{len(state.graph_nodes) + 1}"
|
img_gen_node_id = f"img_gen_{len(state.graph_nodes) + 1}"
|
||||||
|
# Добавляем узел генерации изображений в граф
|
||||||
state.graph_nodes.append({
|
state.graph_nodes.append({
|
||||||
"id": img_gen_node_id,
|
"id": img_gen_node_id,
|
||||||
"type": "tool",
|
"type": "tool",
|
||||||
"data": {
|
"data": {
|
||||||
"label": "Генерация изображений",
|
"label": "Генерация изображений",
|
||||||
"details": prompt
|
"details": prompt,
|
||||||
|
"message": {"role": "assistant", "content": response_text, "node_id": img_gen_node_id}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.graph_edges.append({
|
state.graph_edges.append({
|
||||||
|
|
@ -220,8 +243,8 @@ def generate_images_node(state: AgentState) -> AgentState:
|
||||||
state.parent_node_id = img_gen_node_id
|
state.parent_node_id = img_gen_node_id
|
||||||
state.current_node_id = img_gen_node_id
|
state.current_node_id = img_gen_node_id
|
||||||
|
|
||||||
state.llm_response = "Сгенерированы изображения."
|
# Обновляем временную историю чата
|
||||||
state.chat_history.append({
|
state.temporary_chat_history.append({
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"content": response_text,
|
"content": response_text,
|
||||||
"node_id": img_gen_node_id # Привязываем к ID узла
|
"node_id": img_gen_node_id # Привязываем к ID узла
|
||||||
|
|
@ -231,6 +254,8 @@ def generate_images_node(state: AgentState) -> AgentState:
|
||||||
state.error = f"Ошибка генерации изображений: {e}"
|
state.error = f"Ошибка генерации изображений: {e}"
|
||||||
state.llm_response = f"Произошла ошибка при генерации изображений: {e}"
|
state.llm_response = f"Произошла ошибка при генерации изображений: {e}"
|
||||||
print(state.error)
|
print(state.error)
|
||||||
|
# Добавляем ошибку во временную историю
|
||||||
|
state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -239,11 +264,15 @@ image_analysis_service = ImageAnalysisService(
|
||||||
|
|
||||||
|
|
||||||
def analyze_image_node(state: AgentState) -> AgentState:
|
def analyze_image_node(state: AgentState) -> AgentState:
|
||||||
"""Анализирует изображение по URL или данным."""
|
"""
|
||||||
|
Анализирует изображение по URL и промпту, переданным в команде `/analyze`.
|
||||||
|
"""
|
||||||
print("Выполняется узел: analyze_image_node")
|
print("Выполняется узел: analyze_image_node")
|
||||||
if len(state.command_args) < 2:
|
if len(state.command_args) < 2:
|
||||||
state.error = "Для команды /analyze требуется URL изображения и промпт. Пример: /analyze [url] 'что на изображении?'"
|
state.error = "Для команды /analyze требуется URL изображения и промпт. Пример: /analyze [url] 'что на изображении?'"
|
||||||
state.llm_response = state.error
|
state.llm_response = state.error
|
||||||
|
# Добавляем ошибку во временную историю для согласованности
|
||||||
|
state.temporary_chat_history.append({"role": "assistant", "content": state.error})
|
||||||
return state
|
return state
|
||||||
|
|
||||||
image_url = state.command_args[0]
|
image_url = state.command_args[0]
|
||||||
|
|
@ -253,20 +282,18 @@ def analyze_image_node(state: AgentState) -> AgentState:
|
||||||
analysis_result = image_analysis_service.analyze_image(
|
analysis_result = image_analysis_service.analyze_image(
|
||||||
image_url, prompt)
|
image_url, prompt)
|
||||||
state.analysis_result = analysis_result
|
state.analysis_result = analysis_result
|
||||||
state.llm_response = f"Результат анализа изображения '{image_url}': {analysis_result}"
|
response_text = f"Результат анализа изображения '{image_url}': {analysis_result}"
|
||||||
state.chat_history.append({
|
state.llm_response = response_text
|
||||||
"role": "assistant",
|
|
||||||
"content": state.llm_response
|
|
||||||
})
|
|
||||||
|
|
||||||
# Добавляем узел анализа изображения в граф
|
|
||||||
img_analysis_node_id = f"img_analysis_{len(state.graph_nodes) + 1}"
|
img_analysis_node_id = f"img_analysis_{len(state.graph_nodes) + 1}"
|
||||||
|
# Добавляем узел анализа изображения в граф
|
||||||
state.graph_nodes.append({
|
state.graph_nodes.append({
|
||||||
"id": img_analysis_node_id,
|
"id": img_analysis_node_id,
|
||||||
"type": "tool",
|
"type": "tool",
|
||||||
"data": {
|
"data": {
|
||||||
"label": "Анализ изображения",
|
"label": "Анализ изображения",
|
||||||
"details": f"URL: {image_url}, Промпт: {prompt}"
|
"details": f"URL: {image_url}, Промпт: {prompt}",
|
||||||
|
"message": {"role": "assistant", "content": response_text, "node_id": img_analysis_node_id}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.graph_edges.append({
|
state.graph_edges.append({
|
||||||
|
|
@ -277,10 +304,19 @@ def analyze_image_node(state: AgentState) -> AgentState:
|
||||||
state.parent_node_id = img_analysis_node_id
|
state.parent_node_id = img_analysis_node_id
|
||||||
state.current_node_id = img_analysis_node_id
|
state.current_node_id = img_analysis_node_id
|
||||||
|
|
||||||
|
# Обновляем временную историю чата
|
||||||
|
state.temporary_chat_history.append({
|
||||||
|
"role": "assistant",
|
||||||
|
"content": response_text,
|
||||||
|
"node_id": img_analysis_node_id
|
||||||
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
state.error = f"Ошибка анализа изображения: {e}"
|
state.error = f"Ошибка анализа изображения: {e}"
|
||||||
state.llm_response = f"Произошла ошибка при анализе изображения: {e}"
|
state.llm_response = f"Произошла ошибка при анализе изображения: {e}"
|
||||||
print(state.error)
|
print(state.error)
|
||||||
|
# Добавляем ошибку во временную историю
|
||||||
|
state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -288,25 +324,25 @@ subtitle_service = SubtitleService()
|
||||||
|
|
||||||
|
|
||||||
def get_meet_subtitles_node(state: AgentState) -> AgentState:
|
def get_meet_subtitles_node(state: AgentState) -> AgentState:
|
||||||
"""Получает субтитры из Google Meet."""
|
"""
|
||||||
|
Получает субтитры из Google Meet через команду `/subtitles_meet`.
|
||||||
|
"""
|
||||||
print("Выполняется узел: get_meet_subtitles_node")
|
print("Выполняется узел: get_meet_subtitles_node")
|
||||||
try:
|
try:
|
||||||
subtitles = subtitle_service.get_google_meet_subtitles()
|
subtitles = subtitle_service.get_google_meet_subtitles()
|
||||||
state.subtitles = subtitles
|
state.subtitles = subtitles
|
||||||
state.llm_response = f"Текущие субтитры из Google Meet: {subtitles}"
|
response_text = f"Текущие субтитры из Google Meet: {subtitles}"
|
||||||
state.chat_history.append({
|
state.llm_response = response_text
|
||||||
"role": "assistant",
|
|
||||||
"content": state.llm_response
|
|
||||||
})
|
|
||||||
|
|
||||||
# Добавляем узел субтитров Meet в граф
|
|
||||||
meet_subtitles_node_id = f"meet_subtitles_{len(state.graph_nodes) + 1}"
|
meet_subtitles_node_id = f"meet_subtitles_{len(state.graph_nodes) + 1}"
|
||||||
|
# Добавляем узел субтитров Meet в граф
|
||||||
state.graph_nodes.append({
|
state.graph_nodes.append({
|
||||||
"id": meet_subtitles_node_id,
|
"id": meet_subtitles_node_id,
|
||||||
"type": "tool",
|
"type": "tool",
|
||||||
"data": {
|
"data": {
|
||||||
"label": "Субтитры Google Meet",
|
"label": "Субтитры Google Meet",
|
||||||
"details": subtitles[:30] + "..." if subtitles else ""
|
"details": subtitles[:30] + "..." if subtitles else "",
|
||||||
|
"message": {"role": "assistant", "content": response_text, "node_id": meet_subtitles_node_id}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.graph_edges.append({
|
state.graph_edges.append({
|
||||||
|
|
@ -317,33 +353,42 @@ def get_meet_subtitles_node(state: AgentState) -> AgentState:
|
||||||
state.parent_node_id = meet_subtitles_node_id
|
state.parent_node_id = meet_subtitles_node_id
|
||||||
state.current_node_id = meet_subtitles_node_id
|
state.current_node_id = meet_subtitles_node_id
|
||||||
|
|
||||||
|
# Обновляем временную историю чата
|
||||||
|
state.temporary_chat_history.append({
|
||||||
|
"role": "assistant",
|
||||||
|
"content": response_text,
|
||||||
|
"node_id": meet_subtitles_node_id
|
||||||
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
state.error = f"Ошибка получения субтитров Google Meet: {e}"
|
state.error = f"Ошибка получения субтитров Google Meet: {e}"
|
||||||
state.llm_response = f"Произошла ошибка при получении субтитров Google Meet: {e}"
|
state.llm_response = f"Произошла ошибка при получении субтитров Google Meet: {e}"
|
||||||
print(state.error)
|
print(state.error)
|
||||||
|
# Добавляем ошибку во временную историю
|
||||||
|
state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
||||||
def get_teams_subtitles_node(state: AgentState) -> AgentState:
|
def get_teams_subtitles_node(state: AgentState) -> AgentState:
|
||||||
"""Получает субтитры из MS Teams."""
|
"""
|
||||||
|
Получает субтитры из MS Teams через команду `/subtitles_teams`.
|
||||||
|
"""
|
||||||
print("Выполняется узел: get_teams_subtitles_node")
|
print("Выполняется узел: get_teams_subtitles_node")
|
||||||
try:
|
try:
|
||||||
subtitles = subtitle_service.get_ms_teams_subtitles()
|
subtitles = subtitle_service.get_ms_teams_subtitles()
|
||||||
state.subtitles = subtitles
|
state.subtitles = subtitles
|
||||||
state.llm_response = f"Текущие субтитры из MS Teams: {subtitles}"
|
response_text = f"Текущие субтитры из MS Teams: {subtitles}"
|
||||||
state.chat_history.append({
|
state.llm_response = response_text
|
||||||
"role": "assistant",
|
|
||||||
"content": state.llm_response
|
|
||||||
})
|
|
||||||
|
|
||||||
# Добавляем узел субтитров Teams в граф
|
|
||||||
teams_subtitles_node_id = f"teams_subtitles_{len(state.graph_nodes) + 1}"
|
teams_subtitles_node_id = f"teams_subtitles_{len(state.graph_nodes) + 1}"
|
||||||
|
# Добавляем узел субтитров Teams в граф
|
||||||
state.graph_nodes.append({
|
state.graph_nodes.append({
|
||||||
"id": teams_subtitles_node_id,
|
"id": teams_subtitles_node_id,
|
||||||
"type": "tool",
|
"type": "tool",
|
||||||
"data": {
|
"data": {
|
||||||
"label": "Субтитры MS Teams",
|
"label": "Субтитры MS Teams",
|
||||||
"details": subtitles[:30] + "..." if subtitles else ""
|
"details": subtitles[:30] + "..." if subtitles else "",
|
||||||
|
"message": {"role": "assistant", "content": response_text, "node_id": teams_subtitles_node_id}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.graph_edges.append({
|
state.graph_edges.append({
|
||||||
|
|
@ -354,10 +399,19 @@ def get_teams_subtitles_node(state: AgentState) -> AgentState:
|
||||||
state.parent_node_id = teams_subtitles_node_id
|
state.parent_node_id = teams_subtitles_node_id
|
||||||
state.current_node_id = teams_subtitles_node_id
|
state.current_node_id = teams_subtitles_node_id
|
||||||
|
|
||||||
|
# Обновляем временную историю чата
|
||||||
|
state.temporary_chat_history.append({
|
||||||
|
"role": "assistant",
|
||||||
|
"content": response_text,
|
||||||
|
"node_id": teams_subtitles_node_id
|
||||||
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
state.error = f"Ошибка получения субтитров MS Teams: {e}"
|
state.error = f"Ошибка получения субтитров MS Teams: {e}"
|
||||||
state.llm_response = f"Произошла ошибка при получении субтитров MS Teams: {e}"
|
state.llm_response = f"Произошла ошибка при получении субтитров MS Teams: {e}"
|
||||||
print(state.error)
|
print(state.error)
|
||||||
|
# Добавляем ошибку во временную историю
|
||||||
|
state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -366,28 +420,27 @@ summarization_service = SummarizationService(
|
||||||
|
|
||||||
|
|
||||||
def summarize_history_node(state: AgentState) -> AgentState:
|
def summarize_history_node(state: AgentState) -> AgentState:
|
||||||
"""Суммирует историю диалога."""
|
"""
|
||||||
|
Суммирует историю диалога, используя `temporary_chat_history` в качестве контекста.
|
||||||
|
"""
|
||||||
print("Выполняется узел: summarize_history_node")
|
print("Выполняется узел: summarize_history_node")
|
||||||
try:
|
try:
|
||||||
# Для суммаризации используем полную историю, которую ведет AgentState
|
# Для суммаризации используем временную историю, подготовленную в предыдущих узлах
|
||||||
summarized_text = summarization_service.summarize_history(
|
summarized_text = summarization_service.summarize_history_of_dicts(
|
||||||
state.chat_history)
|
state.temporary_chat_history) # Изменен метод для использования List[Dict]
|
||||||
state.summarized_history_text = summarized_text
|
state.summarized_history_text = summarized_text
|
||||||
state.llm_response = f"История диалога суммирована:\n{summarized_text}"
|
response_text = f"История диалога суммирована:\n{summarized_text}"
|
||||||
state.chat_history.append({
|
state.llm_response = response_text
|
||||||
"role": "assistant",
|
|
||||||
"content": state.llm_response
|
|
||||||
})
|
|
||||||
|
|
||||||
# Добавляем узел суммаризации в граф
|
|
||||||
summary_node_id = f"summary_{len(state.graph_nodes) + 1}"
|
summary_node_id = f"summary_{len(state.graph_nodes) + 1}"
|
||||||
|
# Добавляем узел суммаризации в граф
|
||||||
state.graph_nodes.append({
|
state.graph_nodes.append({
|
||||||
"id": summary_node_id,
|
"id": summary_node_id,
|
||||||
"type": "tool",
|
"type": "tool",
|
||||||
"data": {
|
"data": {
|
||||||
"label": "Суммаризация истории",
|
"label": "Суммаризация истории",
|
||||||
"details":
|
"details": summarized_text[:30] + "..." if summarized_text else "",
|
||||||
summarized_text[:30] + "..." if summarized_text else ""
|
"message": {"role": "assistant", "content": response_text, "node_id": summary_node_id}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.graph_edges.append({
|
state.graph_edges.append({
|
||||||
|
|
@ -398,30 +451,38 @@ def summarize_history_node(state: AgentState) -> AgentState:
|
||||||
state.parent_node_id = summary_node_id
|
state.parent_node_id = summary_node_id
|
||||||
state.current_node_id = summary_node_id
|
state.current_node_id = summary_node_id
|
||||||
|
|
||||||
|
# Обновляем временную историю чата
|
||||||
|
state.temporary_chat_history.append({
|
||||||
|
"role": "assistant",
|
||||||
|
"content": response_text,
|
||||||
|
"node_id": summary_node_id
|
||||||
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
state.error = f"Ошибка суммаризации истории: {e}"
|
state.error = f"Ошибка суммаризации истории: {e}"
|
||||||
state.llm_response = f"Произошла ошибка при суммаризации истории: {e}"
|
state.llm_response = f"Произошла ошибка при суммаризации истории: {e}"
|
||||||
print(state.error)
|
print(state.error)
|
||||||
|
# Добавляем ошибку во временную историю
|
||||||
|
state.temporary_chat_history.append({"role": "assistant", "content": state.llm_response})
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
||||||
def handle_error_node(state: AgentState) -> AgentState:
|
def handle_error_node(state: AgentState) -> AgentState:
|
||||||
"""Обрабатывает ошибки команд."""
|
"""
|
||||||
|
Обрабатывает ошибки команд, формируя ответ для пользователя и добавляя его в граф.
|
||||||
|
"""
|
||||||
print(f"Выполняется узел: handle_error_node. Ошибка: {state.error}")
|
print(f"Выполняется узел: handle_error_node. Ошибка: {state.error}")
|
||||||
state.llm_response = state.error
|
state.llm_response = state.error
|
||||||
state.chat_history.append({
|
|
||||||
"role": "assistant",
|
|
||||||
"content": state.llm_response
|
|
||||||
})
|
|
||||||
|
|
||||||
# Добавляем узел ошибки в граф
|
|
||||||
error_node_id = f"error_{len(state.graph_nodes) + 1}"
|
error_node_id = f"error_{len(state.graph_nodes) + 1}"
|
||||||
|
# Добавляем узел ошибки в граф
|
||||||
state.graph_nodes.append({
|
state.graph_nodes.append({
|
||||||
"id": error_node_id,
|
"id": error_node_id,
|
||||||
"type": "error",
|
"type": "error",
|
||||||
"data": {
|
"data": {
|
||||||
"label": "Ошибка",
|
"label": "Ошибка",
|
||||||
"details": state.error
|
"details": state.error,
|
||||||
|
"message": {"role": "assistant", "content": state.llm_response, "node_id": error_node_id}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.graph_edges.append({
|
state.graph_edges.append({
|
||||||
|
|
@ -432,30 +493,36 @@ def handle_error_node(state: AgentState) -> AgentState:
|
||||||
state.parent_node_id = error_node_id
|
state.parent_node_id = error_node_id
|
||||||
state.current_node_id = error_node_id
|
state.current_node_id = error_node_id
|
||||||
|
|
||||||
|
# Обновляем временную историю чата
|
||||||
|
state.temporary_chat_history.append({
|
||||||
|
"role": "assistant",
|
||||||
|
"content": state.llm_response,
|
||||||
|
"node_id": error_node_id
|
||||||
|
})
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
||||||
def help_node(state: AgentState) -> AgentState:
|
def help_node(state: AgentState) -> AgentState:
|
||||||
"""Предоставляет информацию о доступных командах."""
|
"""
|
||||||
|
Предоставляет информацию о доступных командах, формируя ответ для пользователя и добавляя его в граф.
|
||||||
|
"""
|
||||||
print("Выполняется узел: help_node")
|
print("Выполняется узел: help_node")
|
||||||
commands_info = command_manager.get_commands()
|
commands_info = command_manager.get_commands()
|
||||||
help_text = "Доступные команды:\n"
|
help_text = "Доступные команды:\n"
|
||||||
for cmd, info in commands_info.items():
|
for cmd, info in commands_info.items():
|
||||||
help_text += f"/{cmd}: {info['description']}\n"
|
help_text += f"/{cmd}: {info['description']}\n"
|
||||||
state.llm_response = help_text
|
state.llm_response = help_text
|
||||||
state.chat_history.append({
|
|
||||||
"role": "assistant",
|
|
||||||
"content": state.llm_response
|
|
||||||
})
|
|
||||||
|
|
||||||
# Добавляем узел справки в граф
|
|
||||||
help_node_id = f"help_{len(state.graph_nodes) + 1}"
|
help_node_id = f"help_{len(state.graph_nodes) + 1}"
|
||||||
|
# Добавляем узел справки в граф
|
||||||
state.graph_nodes.append({
|
state.graph_nodes.append({
|
||||||
"id": help_node_id,
|
"id": help_node_id,
|
||||||
"type": "info",
|
"type": "info",
|
||||||
"data": {
|
"data": {
|
||||||
"label": "Справка",
|
"label": "Справка",
|
||||||
"details": "Список команд"
|
"details": "Список команд",
|
||||||
|
"message": {"role": "assistant", "content": state.llm_response, "node_id": help_node_id}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.graph_edges.append({
|
state.graph_edges.append({
|
||||||
|
|
@ -466,4 +533,11 @@ def help_node(state: AgentState) -> AgentState:
|
||||||
state.parent_node_id = help_node_id
|
state.parent_node_id = help_node_id
|
||||||
state.current_node_id = help_node_id
|
state.current_node_id = help_node_id
|
||||||
|
|
||||||
|
# Обновляем временную историю чата
|
||||||
|
state.temporary_chat_history.append({
|
||||||
|
"role": "assistant",
|
||||||
|
"content": state.llm_response,
|
||||||
|
"node_id": help_node_id
|
||||||
|
})
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
|
|
||||||
|
|
@ -97,41 +97,57 @@ def run_agent(user_input: str,
|
||||||
Может продолжить существующий граф по graph_id.
|
Может продолжить существующий граф по graph_id.
|
||||||
Возвращает обновленное состояние и ID графа.
|
Возвращает обновленное состояние и ID графа.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# ---------------------------------------------------- Initial State Setup ----------------------------------------------------------------
|
||||||
initial_state = AgentState(input=user_input, parent_node_id=parent_node_id)
|
initial_state = AgentState(input=user_input, parent_node_id=parent_node_id)
|
||||||
|
|
||||||
# Если есть существующий ID графа, загружаем его историю
|
# Если есть существующий ID графа, загружаем его историю и структуру
|
||||||
|
loaded_graph_data = None
|
||||||
if existing_graph_id:
|
if existing_graph_id:
|
||||||
existing_graph_data = graph_history_manager.get_graph(
|
# Получаем данные графа, включая сгенерированные сообщения до parent_node_id (либо current_node_id из БД)
|
||||||
existing_graph_id)
|
# Если parent_node_id передан, используем его для построения истории.
|
||||||
if existing_graph_data:
|
loaded_graph_data = graph_history_manager.get_graph(
|
||||||
print(f"Продолжаю существующий граф {existing_graph_id}")
|
existing_graph_id, target_node_id=parent_node_id)
|
||||||
initial_state.chat_history = existing_graph_data.get(
|
|
||||||
"messages", [])
|
|
||||||
initial_state.graph_nodes = existing_graph_data.get(
|
|
||||||
"graph_nodes", [])
|
|
||||||
initial_state.graph_edges = existing_graph_data.get(
|
|
||||||
"graph_edges", [])
|
|
||||||
initial_state.parent_node_id = parent_node_id or existing_graph_data.get(
|
|
||||||
"current_node_id")
|
|
||||||
|
|
||||||
|
if loaded_graph_data:
|
||||||
|
print(f"Продолжаю существующий граф {existing_graph_id}")
|
||||||
|
# Загружаем узлы и ребра для продолжения графа
|
||||||
|
initial_state.graph_nodes = loaded_graph_data.get("graph_nodes", [])
|
||||||
|
initial_state.graph_edges = loaded_graph_data.get("graph_edges", [])
|
||||||
|
|
||||||
|
# Устанавливаем parent_node_id для нового входного узла
|
||||||
|
# Если parent_node_id был передан в запросе, используем его,
|
||||||
|
# иначе берем current_node_id из загруженного графа.
|
||||||
|
initial_state.parent_node_id = parent_node_id or loaded_graph_data.get("current_node_id")
|
||||||
|
|
||||||
|
# История чата для текущего раунда работы LLM формируется из загруженных сообщений.
|
||||||
|
initial_state.temporary_chat_history = loaded_graph_data.get("messages", [])
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------- run_agent ----------------------------------------------------------------
|
||||||
# Запускаем граф
|
# Запускаем граф
|
||||||
result = app.invoke(initial_state)
|
result = app.invoke(initial_state)
|
||||||
final_state = AgentState(**result)
|
final_state = AgentState(**result)
|
||||||
|
|
||||||
# Сохраняем обновленное состояние графа
|
# ---------------------------------------------------- Save Graph ----------------------------------------------------------------
|
||||||
|
# Сохраняем обновленное состояние графа.
|
||||||
|
# Обратите внимание: chat_history больше не сохраняется напрямую,
|
||||||
|
# она является частью данных узлов в graph_nodes.
|
||||||
graph_data_to_save = {
|
graph_data_to_save = {
|
||||||
"id": existing_graph_id,
|
"id": existing_graph_id, # Используем существующий ID или он будет сгенерирован
|
||||||
"messages": final_state.chat_history,
|
# менеджером истории при первом сохранении
|
||||||
"graph_nodes": final_state.graph_nodes,
|
"graph_nodes": final_state.graph_nodes,
|
||||||
"graph_edges": final_state.graph_edges,
|
"graph_edges": final_state.graph_edges,
|
||||||
"current_node_id": final_state.current_node_id
|
"current_node_id": final_state.current_node_id
|
||||||
}
|
}
|
||||||
new_graph_id = graph_history_manager.save_graph(graph_data_to_save)
|
new_graph_id = graph_history_manager.save_graph(graph_data_to_save)
|
||||||
|
|
||||||
# Формируем ответ для фронтенда
|
# ---------------------------------------------------- prepare response ----------------------------------------------------------------
|
||||||
|
# Для `messages` в `response_data` используем `final_state.temporary_chat_history`,
|
||||||
|
# так как она отражает только сообщения текущей ветки, добавленные в ходе этого выполнения.
|
||||||
response_data = {
|
response_data = {
|
||||||
"graph_id": new_graph_id,
|
"graph_id": new_graph_id,
|
||||||
"messages": final_state.chat_history,
|
"messages": final_state.temporary_chat_history,
|
||||||
"llm_response": final_state.llm_response,
|
"llm_response": final_state.llm_response,
|
||||||
"image_urls": final_state.image_urls,
|
"image_urls": final_state.image_urls,
|
||||||
"analysis_result": final_state.analysis_result,
|
"analysis_result": final_state.analysis_result,
|
||||||
|
|
|
||||||
BIN
graph_history.db
BIN
graph_history.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user