bug fixes for correct loading of chat and graph panels

This commit is contained in:
dimitrievgs 2025-09-16 01:38:27 +03:00
parent 2bf5763eb1
commit 8bf1394e6f
4 changed files with 128 additions and 141 deletions

19
main.js

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,6 @@ export class HistoryPanel {
container: HTMLElement; container: HTMLElement;
props: { props: {
graphs: any[]; graphs: any[];
onGraphSelect: (data: { graphId: string; currentNode: string | null }) => void;
eventBus: EventBus; eventBus: EventBus;
}; };
graphs: any[]; graphs: any[];
@ -23,7 +22,6 @@ export class HistoryPanel {
container: HTMLElement, container: HTMLElement,
props: { props: {
graphs: any[]; graphs: any[];
onGraphSelect: (data: { graphId: string; currentNode: string | null }) => void;
eventBus: EventBus; eventBus: EventBus;
} }
) { ) {
@ -103,8 +101,8 @@ export class HistoryPanel {
item.addEventListener('click', (e) => { item.addEventListener('click', (e) => {
if (!(e.target as HTMLElement).classList.contains('chat-item-options-button')) { if (!(e.target as HTMLElement).classList.contains('chat-item-options-button')) {
const graphId = (item as HTMLElement).dataset.graphId; const graphId = (item as HTMLElement).dataset.graphId;
if (graphId && this.props.onGraphSelect) { if (graphId) {
this.props.onGraphSelect({ graphId: graphId, currentNode: null }); this.props.eventBus.emit('graph-selected', { graphId: graphId, currentNode: null });
} }
} }
}); });

View File

@ -55,10 +55,10 @@ export class ChatView extends ItemView {
}); });
// Подписываемся на события // Подписываемся на события
this.plugin.eventBus.on('node-selected', this.handleNodeSelected.bind(this)); this.plugin.eventBus.on('node-selected', this.handleNodeSelected.bind(this));
this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this)); this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this));
this.plugin.eventBus.on('graph-selected', this.handleGraphSelected.bind(this)); this.plugin.eventBus.on('graph-selected', this.handleGraphSelected.bind(this));
} }
async onClose(): Promise<void> { async onClose(): Promise<void> {
if (this.chatPanel) { if (this.chatPanel) {
@ -72,133 +72,130 @@ export class ChatView extends ItemView {
// ----------------------------------------------- API Methods --------------------------------------------------------------- // ----------------------------------------------- API Methods ---------------------------------------------------------------
// TODO: проблемное место // TODO: проблемное место
/** /**
* Загружает историю сообщений от корня до указанного узла графа. * Загружает историю сообщений от корня до указанного узла графа.
* Обновляет `chatHistory` и отображение в `chatPanel`. * Обновляет `chatHistory` и отображение в `chatPanel`.
* @param {string} nodeId - ID конечного узла для загрузки истории. * @param {string} nodeId - ID конечного узла для загрузки истории.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async fetchMessagesFromRootToNode(nodeId: string): Promise<void> { async fetchMessagesFromRootToNode(nodeId: string | null): Promise<void> {
try { try {
// Убедимся, что selectedGraphId установлен // Убедимся, что selectedGraphId установлен
if (!this.selectedGraphId) { if (!this.selectedGraphId) {
console.warn('selectedGraphId не установлен. Невозможно загрузить сообщения.'); console.warn('selectedGraphId не установлен. Невозможно загрузить сообщения.');
return; return;
} }
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/${nodeId}`);
if (!response.ok) { const url = nodeId
const errorText = await response.text(); ? `${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/${nodeId}`
throw new Error(`Ошибка загрузки сообщений: ${response.status} - ${errorText}`); : `${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/last`;
} const response = await fetch(url);
const data = await response.json();
// ИСПРАВЛЕНО: Убедиться, что type передается, если это возможно if (!response.ok) {
this.chatHistory = data.map((msg: any) => ({ const errorText = await response.text();
...msg, throw new Error(`Ошибка загрузки сообщений: ${response.status} - ${errorText}`);
type: msg.type || msg.role // Используем type, если есть, иначе role как fallback }
})) || []; const data = await response.json();
if (this.chatPanel) { // ИСПРАВЛЕНО: Убедиться, что type передается, если это возможно
this.chatPanel.updateHistory(this.chatHistory); this.chatHistory =
} data.map((msg: any) => ({
} catch (error) { ...msg,
console.error('Ошибка загрузки сообщений:', error); type: msg.type || msg.role // Используем type, если есть, иначе role как fallback
// Optionally: show a notice to the user })) || [];
// new Notice(`Ошибка загрузки сообщений: ${error.message}`); if (this.chatPanel) {
} this.chatPanel.updateHistory(this.chatHistory);
} }
} catch (error) {
console.error('Ошибка загрузки сообщений:', error);
// Optionally: show a notice to the user
// new Notice(`Ошибка загрузки сообщений: ${error.message}`);
}
}
// ----------------------------------------------- Event Handlers --------------------------------------------------------------- // ----------------------------------------------- Event Handlers ---------------------------------------------------------------
async handleSendMessage(message: string): Promise<void> { async handleSendMessage(message: string): Promise<void> {
try { try {
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, { const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
message: message, message: message,
graph_id: this.selectedGraphId, graph_id: this.selectedGraphId,
parent_node_id: this.currentNode, parent_node_id: this.currentNode
}), })
}); });
if (!response.ok) { if (!response.ok) {
// Пытаемся прочитать ошибку из тела ответа, если это возможно // Пытаемся прочитать ошибку из тела ответа, если это возможно
const errorData = await response.json().catch(() => ({ message: response.statusText })); const errorData = await response.json().catch(() => ({ message: response.statusText }));
throw new Error(`HTTP ошибка при отправке сообщения! Статус: ${response.status} - ${errorData.message}`); throw new Error(`HTTP ошибка при отправке сообщения! Статус: ${response.status} - ${errorData.message}`);
} }
const result = await response.json(); const result = await response.json();
// Обновляем graph_id и current_node_id всегда
this.selectedGraphId = result.graph_id;
const newCurrentNodeId = result.graph_visualization_data.current_node_id;
this.currentNode = newCurrentNodeId;
// Сначала обновим GraphView, чтобы он запросил последнюю версию графа // Обновляем graph_id и current_node_id всегда
// Это предотвратит состояние гонки, когда разные запросы возвращают частичные графы. this.selectedGraphId = result.graph_id;
// Вместо того, чтобы отправлять частичные данные, мы запрашиваем полный граф у бэкенда. const newCurrentNodeId = result.graph_visualization_data.current_node_id;
this.plugin.eventBus.emit('graph-selected', { graphId: this.selectedGraphId, currentNode: this.currentNode }); this.currentNode = newCurrentNodeId;
// Затем обновим ChatView, загрузив историю до нового current_node_id
if (newCurrentNodeId) {
await this.fetchMessagesFromRootToNode(newCurrentNodeId);
} else if (this.selectedGraphId) {
// Если newCurrentNodeId по какой-то причине не вернулся, но graph_id есть,
// пытаемся загрузить историю по последнему известному узлу или просто весь граф (чтобы отобразить пустую историю, если граф новый).
// В этом случае, если current_node_id отсутствует, historyPanel должна очиститься.
this.currentNode = null; // Сбрасываем, если no current_node_id
this.fetchMessagesFromRootToNode(''); // Передаем пустую строку или null, чтобы очистить историю
}
} catch (error) { // Сначала обновим GraphView, чтобы он запросил последнюю версию графа
console.error('Ошибка отправки сообщения:', error); // Это предотвратит состояние гонки, когда разные запросы возвращают частичные графы.
} // Вместо того, чтобы отправлять частичные данные, мы запрашиваем полный граф у бэкенда.
} this.plugin.eventBus.emit('graph-selected', { graphId: this.selectedGraphId, currentNode: this.currentNode });
/** // Затем обновим ChatView, загрузив историю до нового current_node_id
* Обрабатывает выбор узла на графе. Загружает историю сообщений до выбранного узла if (newCurrentNodeId) {
* и обновляет панель чата. await this.fetchMessagesFromRootToNode(newCurrentNodeId);
* @param {{ nodeId: string }} data - Объект, содержащий ID выбранного узла. } else if (this.selectedGraphId) {
*/ // Если newCurrentNodeId по какой-то причине не вернулся, но graph_id есть,
handleNodeSelected(data: { nodeId: string }): void { // пытаемся загрузить историю по последнему известному узлу или просто весь граф (чтобы отобразить пустую историю, если граф новый).
this.currentNode = data.nodeId; // В этом случае, если current_node_id отсутствует, historyPanel должна очиститься.
if (this.selectedGraphId) { this.currentNode = null; // Сбрасываем, если no current_node_id
this.fetchMessagesFromRootToNode(data.nodeId); this.fetchMessagesFromRootToNode(''); // Передаем пустую строку или null, чтобы очистить историю
} }
} } catch (error) {
console.error('Ошибка отправки сообщения:', error);
}
}
/** /**
* Обрабатывает событие начала нового чата. Сбрасывает текущее состояние чата * Обрабатывает выбор узла на графе. Загружает историю сообщений до выбранного узла
* и очищает отображение истории. * и обновляет панель чата.
*/ * @param {{ nodeId: string }} data - Объект, содержащий ID выбранного узла.
handleNewChat(): void { */
this.chatHistory = []; handleNodeSelected(data: { nodeId: string }): void {
this.currentNode = null; this.currentNode = data.nodeId;
this.selectedGraphId = null; if (this.selectedGraphId) {
if (this.chatPanel) { this.fetchMessagesFromRootToNode(data.nodeId);
this.chatPanel.updateHistory(this.chatHistory); }
} }
}
/** /**
* Обрабатывает выбор графа из `GraphView`. Устанавливает выбранный граф и обновляет * Обрабатывает событие начала нового чата. Сбрасывает текущее состояние чата
* историю чата, загружая сообщения, относящиеся к этому графу. * и очищает отображение истории.
* @param {{ graphId: string; currentNode: string | null }} data - Объект, содержащий ID выбранного графа и текущий узел. */
* @returns {Promise<void>} handleNewChat(): void {
*/ this.chatHistory = [];
async handleGraphSelected(data: { graphId: string; currentNode: string | null }): Promise<void> { this.currentNode = null;
this.selectedGraphId = data.graphId; this.selectedGraphId = null;
this.currentNode = data.currentNode; if (this.chatPanel) {
if (this.currentNode) { this.chatPanel.updateHistory(this.chatHistory);
await this.fetchMessagesFromRootToNode(this.currentNode); }
} else { }
// Если текущего узла нет (например, пустой граф), очищаем историю
this.chatHistory = []; /**
if (this.chatPanel) { * Обрабатывает выбор графа из `GraphView`. Устанавливает выбранный граф и обновляет
this.chatPanel.updateHistory(this.chatHistory); * историю чата, загружая сообщения, относящиеся к этому графу.
} * @param {{ graphId: string; currentNode: string | null }} data - Объект, содержащий ID выбранного графа и текущий узел.
} * @returns {Promise<void>}
} */
async handleGraphSelected(data: { graphId: string; currentNode: string | null }): Promise<void> {
this.selectedGraphId = data.graphId;
this.currentNode = data.currentNode; // Может быть пустым, но это не проблема
await this.fetchMessagesFromRootToNode(data.currentNode);
}
} }

View File

@ -99,7 +99,6 @@ export class GraphView extends ItemView {
// Инициализируем панель истории // Инициализируем панель истории
this.historyPanel = new HistoryPanel(this.historyContainer, { this.historyPanel = new HistoryPanel(this.historyContainer, {
graphs: this.graphsList, graphs: this.graphsList,
onGraphSelect: this.handleGraphSelected.bind(this),
eventBus: this.plugin.eventBus eventBus: this.plugin.eventBus
}); });