diff --git a/app/api.py b/app/api.py index 96a53f1..1a0e538 100644 --- a/app/api.py +++ b/app/api.py @@ -5,10 +5,10 @@ """ import json -from flask import Flask, request, jsonify +from flask import Flask, request, jsonify, Response, stream_with_context from flask_cors import CORS -from flask_socketio import SocketIO, emit -from workflows import run_agent, graph_history_manager, run_agent_streaming +from flask_socketio import SocketIO +from workflows import graph_history_manager, run_agent_streaming from llm_client import MODELS # Добавляем импорт списка моделей api = Flask(__name__) @@ -37,25 +37,6 @@ def handle_disconnect(): # ----------------------------------------------- API Endpoints --------------------------------------------------------------- -@api.route('/api/chat', methods=['POST']) -def chat(): - """API endpoint для обработки сообщений и возврата ответа.""" - data = request.get_json() - message = data.get("message") - graph_id = data.get("graph_id") - parent_node_id = data.get( - "parent_node_id") # Получаем parent_node_id из запроса - system_prompt = data.get("system_prompt") - model = data.get("model") - - if not message: - return jsonify({"error": "Сообщение не может быть пустым."}, 400) - - result = run_agent(message, graph_id, - parent_node_id, system_prompt, model) # Передаем parent_node_id в run_agent - return jsonify(result) - - @api.route('/api/graphs', methods=['GET']) def get_graphs(): """API endpoint для получения списка графов.""" @@ -143,8 +124,6 @@ def send_user_message(): return jsonify({"error": str(e)}, 500) -from flask import Response, stream_with_context - @api.route('/api/chat/stream', methods=['POST']) def chat_stream(): """API endpoint для стриминга ответа LLM.""" @@ -197,8 +176,8 @@ def regenerate_message(): data = request.get_json() graph_id = data.get("graph_id") node_id = data.get("node_id") - system_prompt = data.get("system_prompt") - model = data.get("model") + '''system_prompt = data.get("system_prompt") + model = data.get("model")''' if not graph_id or not node_id: return jsonify({"error": "Не указаны graph_id или node_id."}, 400) diff --git a/app/graph_history_manager.py b/app/graph_history_manager.py index 9dfdc46..983d33b 100644 --- a/app/graph_history_manager.py +++ b/app/graph_history_manager.py @@ -156,68 +156,6 @@ class GraphHistoryManager: (graph_id, edge["id"], edge["source"], edge["target"])) # ----------------------------------------------- Public Methods ------------------------------------------------------------ - def save_graph_changes(self, graph_id: str, new_nodes: List[Dict[str, - Any]], - new_edges: List[Dict[str, Any]], - current_node_id: str) -> str: - """ - Сохраняет *изменения* в граф в базе данных. - Добавляет новые узлы и ребра и обновляет current_node_id. - Операции проводятся в рамках одной транзакции. - Конфликты при добавлении существующих узлов/ребер игнорируются. - Если graph_id пуст, генерируется новый UUID для графа. - """ - if not graph_id: - graph_id = str(uuid.uuid4()) # Генерируем новый UUID для ID графа - - with self._get_connection( - ) as conn: # Одна транзакция для всех изменений - try: - # Убедимся, что запись о графе существует в главной таблице `graphs` - # INSERT OR IGNORE создаст новую запись, если её нет. - # Если граф уже существует, это ничего не изменит. - conn.execute("INSERT OR IGNORE INTO graphs (id) VALUES (?)", - (graph_id, )) - - # Проверяем, новый ли это граф - cursor = conn.cursor() - cursor.execute( - "SELECT COUNT(*) FROM graph_nodes_data WHERE graph_id = ?", - (graph_id, )) - existing_nodes_count = cursor.fetchone()[0] - is_new_graph = existing_nodes_count == 0 - - # Добавляем новые узлы - for node in new_nodes: - self._add_graph_node(conn, graph_id, node) - self.title_generator.add_node_to_queue(cursor, graph_id, node["id"]) - - # Добавляем новые ребра - for edge in new_edges: - self._add_graph_edge(conn, graph_id, edge) - - # Обновляем current_node_id - это атомарное изменение в рамках транзакции - self._update_graph_current_node_id(conn, graph_id, - current_node_id) - - # Если новый граф, добавляем его в очередь заголовков - if is_new_graph: - self.title_generator.add_graph_to_queue(cursor, graph_id) - - conn.commit() # Фиксируем все изменения - print(f"Изменения графа {graph_id} сохранены.") - return graph_id - except sqlite3.OperationalError as e: - conn.rollback( - ) # Откатываем транзакцию при ошибке (например, DB Locked) - print( - f"Ошибка блокировки SQLite при сохранении изменений графа {graph_id}: {e}" - ) - raise - except Exception as e: - conn.rollback() # Откатываем транзакцию при других ошибках - print(f"Ошибка при сохранении изменений графа {graph_id}: {e}") - raise def get_graph( self, diff --git a/app/llm_client.py b/app/llm_client.py index d16f900..79a25da 100644 --- a/app/llm_client.py +++ b/app/llm_client.py @@ -3,7 +3,6 @@ включая OpenAI (Gemini) и MistralAI, обеспечивая унифицированный интерфейс. """ -import requests from typing import Dict, Any, List from langchain_core.messages import HumanMessage, SystemMessage, AIMessage diff --git a/app/workflows.py b/app/workflows.py index ee79fb6..ed4485f 100644 --- a/app/workflows.py +++ b/app/workflows.py @@ -91,111 +91,6 @@ workflow.add_edge("handle_error", app = workflow.compile() -def run_agent(user_input: str, - existing_graph_id: Optional[str] = None, - parent_node_id: Optional[str] = None, - system_prompt: Optional[str] = None, - model: Optional[str] = None) -> Dict[str, Any]: - """ - Запускает агента с заданным пользовательским вводом. - Может продолжить существующий граф по graph_id. - Возвращает обновленное состояние и ID графа. - """ - - # ---------------------------------------------------- Initial State Setup ---------------------------------------------------------------- - initial_state = AgentState(input=user_input, - parent_node_id=parent_node_id, - system_prompt=system_prompt, - selected_model=model or "gemini-2.5-flash") - - # Сохраняем исходные ID узлов и ребер для определения новых после выполнения графа - original_node_ids: Set[str] = set() - original_edge_ids: Set[str] = set() - - # Если есть существующий ID графа, загружаем его историю и структуру - loaded_graph_data = None - if existing_graph_id: - # Получаем данные графа, включая сгенерированные сообщения до parent_node_id (либо current_node_id из БД) - # Если parent_node_id передан, используем его для построения истории. - loaded_graph_data = graph_history_manager.get_graph( - existing_graph_id, target_node_id=parent_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", []) - - # Сохраняем ID загруженных узлов и ребер - original_node_ids = { - node['id'] - for node in initial_state.graph_nodes - } - original_edge_ids = { - edge['id'] - for edge in initial_state.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", []) - - # Определяем graph_id, который будет использоваться для сохранения. - # Если существующий ID не передан, GraphHistoryManager сгенерирует новый. - graph_id_to_save = existing_graph_id - - # ---------------------------------------------------- run_agent ---------------------------------------------------------------- - # Запускаем граф - result = app.invoke(initial_state) - final_state = AgentState(**result) - - # ---------------------------------------------------- Save Graph ---------------------------------------------------------------- - # Определяем новые узлы и ребра для сохранения - newly_added_nodes = [ - node for node in final_state.graph_nodes - if node['id'] not in original_node_ids - ] - newly_added_edges = [ - edge for edge in final_state.graph_edges - if edge['id'] not in original_edge_ids - ] - - # Сохраняем обновленное состояние графа. - # Обратите внимание: chat_history больше не сохраняется напрямую, - # она является частью данных узлов в graph_nodes. - final_graph_id = graph_history_manager.save_graph_changes( - graph_id_to_save, newly_added_nodes, newly_added_edges, - final_state.current_node_id) - - # ---------------------------------------------------- prepare response ---------------------------------------------------------------- - # Для `messages` в `response_data` используем `final_state.temporary_chat_history`, - # так как она отражает только сообщения текущей ветки, добавленные в ходе этого выполнения. - response_data = { - "graph_id": final_graph_id, - "messages": final_state.temporary_chat_history, - "llm_response": final_state.llm_response, - "image_urls": final_state.image_urls, - "analysis_result": final_state.analysis_result, - "subtitles": final_state.subtitles, - "summarized_history_text": final_state.summarized_history_text, - "error": final_state.error, - "graph_visualization_data": { - "nodes": final_state.graph_nodes, - "edges": final_state.graph_edges, - "current_node_id": final_state.current_node_id - } - } - return response_data - - def run_agent_streaming(graph_id: str, user_node_id: str, assistant_node_id: str,