feat(llm): redo chat storage, part 1

This commit is contained in:
perf3ct 2025-06-02 00:56:19 +00:00
parent 206905b278
commit 35f78aede9
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232
6 changed files with 142 additions and 104 deletions

View File

@ -6,8 +6,10 @@ import type { SessionResponse } from "./types.js";
/** /**
* Create a new chat session * Create a new chat session
* @param currentNoteId - Optional current note ID for context
* @returns The noteId of the created chat note
*/ */
export async function createChatSession(currentNoteId?: string): Promise<{chatNoteId: string | null, noteId: string | null}> { export async function createChatSession(currentNoteId?: string): Promise<string | null> {
try { try {
const resp = await server.post<SessionResponse>('llm/chat', { const resp = await server.post<SessionResponse>('llm/chat', {
title: 'Note Chat', title: 'Note Chat',
@ -15,48 +17,42 @@ export async function createChatSession(currentNoteId?: string): Promise<{chatNo
}); });
if (resp && resp.id) { if (resp && resp.id) {
// The backend might provide the noteId separately from the chatNoteId // Backend returns the chat note ID as 'id'
// If noteId is provided, use it; otherwise, we'll need to query for it separately return resp.id;
return {
chatNoteId: resp.id,
noteId: resp.noteId || null
};
} }
} catch (error) { } catch (error) {
console.error('Failed to create chat session:', error); console.error('Failed to create chat session:', error);
} }
return { return null;
chatNoteId: null,
noteId: null
};
} }
/** /**
* Check if a session exists * Check if a chat note exists
* @param noteId - The ID of the chat note
*/ */
export async function checkSessionExists(chatNoteId: string): Promise<boolean> { export async function checkSessionExists(noteId: string): Promise<boolean> {
try { try {
// Validate that we have a proper note ID format, not a session ID const sessionCheck = await server.getWithSilentNotFound<any>(`llm/chat/${noteId}`);
// Note IDs in Trilium are typically longer or in a different format
if (chatNoteId && chatNoteId.length === 16 && /^[A-Za-z0-9]+$/.test(chatNoteId)) {
console.warn(`Invalid note ID format detected: ${chatNoteId} appears to be a legacy session ID`);
return false;
}
const sessionCheck = await server.getWithSilentNotFound<any>(`llm/chat/${chatNoteId}`);
return !!(sessionCheck && sessionCheck.id); return !!(sessionCheck && sessionCheck.id);
} catch (error: any) { } catch (error: any) {
console.log(`Error checking chat note ${chatNoteId}:`, error); console.log(`Error checking chat note ${noteId}:`, error);
return false; return false;
} }
} }
/** /**
* Set up streaming response via WebSocket * Set up streaming response via WebSocket
* @param noteId - The ID of the chat note
* @param messageParams - Message parameters
* @param onContentUpdate - Callback for content updates
* @param onThinkingUpdate - Callback for thinking updates
* @param onToolExecution - Callback for tool execution
* @param onComplete - Callback for completion
* @param onError - Callback for errors
*/ */
export async function setupStreamingResponse( export async function setupStreamingResponse(
chatNoteId: string, noteId: string,
messageParams: any, messageParams: any,
onContentUpdate: (content: string, isDone?: boolean) => void, onContentUpdate: (content: string, isDone?: boolean) => void,
onThinkingUpdate: (thinking: string) => void, onThinkingUpdate: (thinking: string) => void,
@ -64,13 +60,6 @@ export async function setupStreamingResponse(
onComplete: () => void, onComplete: () => void,
onError: (error: Error) => void onError: (error: Error) => void
): Promise<void> { ): Promise<void> {
// Validate that we have a proper note ID format, not a session ID
if (chatNoteId && chatNoteId.length === 16 && /^[A-Za-z0-9]+$/.test(chatNoteId)) {
console.error(`Invalid note ID format: ${chatNoteId} appears to be a legacy session ID`);
onError(new Error("Invalid note ID format - using a legacy session ID"));
return;
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let assistantResponse = ''; let assistantResponse = '';
let postToolResponse = ''; // Separate accumulator for post-tool execution content let postToolResponse = ''; // Separate accumulator for post-tool execution content
@ -87,12 +76,12 @@ export async function setupStreamingResponse(
// Create a unique identifier for this response process // Create a unique identifier for this response process
const responseId = `llm-stream-${Date.now()}-${Math.floor(Math.random() * 1000)}`; const responseId = `llm-stream-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
console.log(`[${responseId}] Setting up WebSocket streaming for chat note ${chatNoteId}`); console.log(`[${responseId}] Setting up WebSocket streaming for chat note ${noteId}`);
// Send the initial request to initiate streaming // Send the initial request to initiate streaming
(async () => { (async () => {
try { try {
const streamResponse = await server.post<any>(`llm/chat/${chatNoteId}/messages/stream`, { const streamResponse = await server.post<any>(`llm/chat/${noteId}/messages/stream`, {
content: messageParams.content, content: messageParams.content,
useAdvancedContext: messageParams.useAdvancedContext, useAdvancedContext: messageParams.useAdvancedContext,
showThinking: messageParams.showThinking, showThinking: messageParams.showThinking,
@ -158,7 +147,7 @@ export async function setupStreamingResponse(
const message = customEvent.detail; const message = customEvent.detail;
// Only process messages for our chat note // Only process messages for our chat note
if (!message || message.chatNoteId !== chatNoteId) { if (!message || message.chatNoteId !== noteId) {
return; return;
} }
@ -172,12 +161,12 @@ export async function setupStreamingResponse(
cleanupTimeoutId = null; cleanupTimeoutId = null;
} }
console.log(`[${responseId}] LLM Stream message received via CustomEvent: chatNoteId=${chatNoteId}, content=${!!message.content}, contentLength=${message.content?.length || 0}, thinking=${!!message.thinking}, toolExecution=${!!message.toolExecution}, done=${!!message.done}, type=${message.type || 'llm-stream'}`); console.log(`[${responseId}] LLM Stream message received via CustomEvent: chatNoteId=${noteId}, content=${!!message.content}, contentLength=${message.content?.length || 0}, thinking=${!!message.thinking}, toolExecution=${!!message.toolExecution}, done=${!!message.done}, type=${message.type || 'llm-stream'}`);
// Mark first message received // Mark first message received
if (!receivedAnyMessage) { if (!receivedAnyMessage) {
receivedAnyMessage = true; receivedAnyMessage = true;
console.log(`[${responseId}] First message received for chat note ${chatNoteId}`); console.log(`[${responseId}] First message received for chat note ${noteId}`);
// Clear the initial timeout since we've received a message // Clear the initial timeout since we've received a message
if (initialTimeoutId !== null) { if (initialTimeoutId !== null) {
@ -298,7 +287,7 @@ export async function setupStreamingResponse(
// Set new timeout // Set new timeout
timeoutId = window.setTimeout(() => { timeoutId = window.setTimeout(() => {
console.warn(`[${responseId}] Stream timeout for chat note ${chatNoteId}`); console.warn(`[${responseId}] Stream timeout for chat note ${noteId}`);
// Clean up // Clean up
performCleanup(); performCleanup();
@ -369,7 +358,7 @@ export async function setupStreamingResponse(
// Handle completion // Handle completion
if (message.done) { if (message.done) {
console.log(`[${responseId}] Stream completed for chat note ${chatNoteId}, has content: ${!!message.content}, content length: ${message.content?.length || 0}, current response: ${assistantResponse.length} chars`); console.log(`[${responseId}] Stream completed for chat note ${noteId}, has content: ${!!message.content}, content length: ${message.content?.length || 0}, current response: ${assistantResponse.length} chars`);
// Dump message content to console for debugging // Dump message content to console for debugging
if (message.content) { if (message.content) {
@ -428,9 +417,9 @@ export async function setupStreamingResponse(
// Set initial timeout for receiving any message // Set initial timeout for receiving any message
initialTimeoutId = window.setTimeout(() => { initialTimeoutId = window.setTimeout(() => {
console.warn(`[${responseId}] No messages received for initial period in chat note ${chatNoteId}`); console.warn(`[${responseId}] No messages received for initial period in chat note ${noteId}`);
if (!receivedAnyMessage) { if (!receivedAnyMessage) {
console.error(`[${responseId}] WebSocket connection not established for chat note ${chatNoteId}`); console.error(`[${responseId}] WebSocket connection not established for chat note ${noteId}`);
if (timeoutId !== null) { if (timeoutId !== null) {
window.clearTimeout(timeoutId); window.clearTimeout(timeoutId);
@ -463,15 +452,9 @@ function cleanupEventListener(listener: ((event: Event) => void) | null): void {
/** /**
* Get a direct response from the server without streaming * Get a direct response from the server without streaming
*/ */
export async function getDirectResponse(chatNoteId: string, messageParams: any): Promise<any> { export async function getDirectResponse(noteId: string, messageParams: any): Promise<any> {
try { try {
// Validate that we have a proper note ID format, not a session ID const postResponse = await server.post<any>(`llm/chat/${noteId}/messages`, {
if (chatNoteId && chatNoteId.length === 16 && /^[A-Za-z0-9]+$/.test(chatNoteId)) {
console.error(`Invalid note ID format: ${chatNoteId} appears to be a legacy session ID`);
throw new Error("Invalid note ID format - using a legacy session ID");
}
const postResponse = await server.post<any>(`llm/chat/${chatNoteId}/messages`, {
message: messageParams.content, message: messageParams.content,
includeContext: messageParams.useAdvancedContext, includeContext: messageParams.useAdvancedContext,
options: { options: {

View File

@ -37,9 +37,10 @@ export default class LlmChatPanel extends BasicWidget {
private thinkingBubble!: HTMLElement; private thinkingBubble!: HTMLElement;
private thinkingText!: HTMLElement; private thinkingText!: HTMLElement;
private thinkingToggle!: HTMLElement; private thinkingToggle!: HTMLElement;
private chatNoteId: string | null = null;
private noteId: string | null = null; // The actual noteId for the Chat Note // Simplified to just use noteId - this represents the AI Chat note we're working with
private currentNoteId: string | null = null; private noteId: string | null = null;
private currentNoteId: string | null = null; // The note providing context (for regular notes)
private _messageHandlerId: number | null = null; private _messageHandlerId: number | null = null;
private _messageHandler: any = null; private _messageHandler: any = null;
@ -90,12 +91,21 @@ export default class LlmChatPanel extends BasicWidget {
this.messages = messages; this.messages = messages;
} }
public getChatNoteId(): string | null { public getNoteId(): string | null {
return this.chatNoteId; return this.noteId;
} }
public setChatNoteId(chatNoteId: string | null): void { public setNoteId(noteId: string | null): void {
this.chatNoteId = chatNoteId; this.noteId = noteId;
}
// Deprecated - keeping for backward compatibility but mapping to noteId
public getChatNoteId(): string | null {
return this.noteId;
}
public setChatNoteId(noteId: string | null): void {
this.noteId = noteId;
} }
public getNoteContextChatMessages(): HTMLElement { public getNoteContextChatMessages(): HTMLElement {
@ -307,10 +317,16 @@ export default class LlmChatPanel extends BasicWidget {
} }
} }
const dataToSave: ChatData = { // Only save if we have a valid note ID
if (!this.noteId) {
console.warn('Cannot save chat data: no noteId available');
return;
}
const dataToSave = {
messages: this.messages, messages: this.messages,
chatNoteId: this.chatNoteId,
noteId: this.noteId, noteId: this.noteId,
chatNoteId: this.noteId, // For backward compatibility
toolSteps: toolSteps, toolSteps: toolSteps,
// Add sources if we have them // Add sources if we have them
sources: this.sources || [], sources: this.sources || [],
@ -325,7 +341,7 @@ export default class LlmChatPanel extends BasicWidget {
} }
}; };
console.log(`Saving chat data with chatNoteId: ${this.chatNoteId}, noteId: ${this.noteId}, ${toolSteps.length} tool steps, ${this.sources?.length || 0} sources, ${toolExecutions.length} tool executions`); console.log(`Saving chat data with noteId: ${this.noteId}, ${toolSteps.length} tool steps, ${this.sources?.length || 0} sources, ${toolExecutions.length} tool executions`);
// Save the data to the note attribute via the callback // Save the data to the note attribute via the callback
// This is the ONLY place we should save data, letting the container widget handle persistence // This is the ONLY place we should save data, letting the container widget handle persistence
@ -400,7 +416,6 @@ export default class LlmChatPanel extends BasicWidget {
// Load Chat Note ID if available // Load Chat Note ID if available
if (savedData.noteId) { if (savedData.noteId) {
console.log(`Using noteId as Chat Note ID: ${savedData.noteId}`); console.log(`Using noteId as Chat Note ID: ${savedData.noteId}`);
this.chatNoteId = savedData.noteId;
this.noteId = savedData.noteId; this.noteId = savedData.noteId;
} else { } else {
console.log(`No noteId found in saved data, cannot load chat session`); console.log(`No noteId found in saved data, cannot load chat session`);
@ -550,6 +565,15 @@ export default class LlmChatPanel extends BasicWidget {
// Get current note context if needed // Get current note context if needed
const currentActiveNoteId = appContext.tabManager.getActiveContext()?.note?.noteId || null; const currentActiveNoteId = appContext.tabManager.getActiveContext()?.note?.noteId || null;
// For AI Chat notes, the note itself IS the chat session
// So currentNoteId and noteId should be the same
if (this.noteId && currentActiveNoteId === this.noteId) {
// We're in an AI Chat note - don't reset, just load saved data
console.log(`Refreshing AI Chat note ${this.noteId} - loading saved data`);
await this.loadSavedData();
return;
}
// If we're switching to a different note, we need to reset // If we're switching to a different note, we need to reset
if (this.currentNoteId !== currentActiveNoteId) { if (this.currentNoteId !== currentActiveNoteId) {
console.log(`Note ID changed from ${this.currentNoteId} to ${currentActiveNoteId}, resetting chat panel`); console.log(`Note ID changed from ${this.currentNoteId} to ${currentActiveNoteId}, resetting chat panel`);
@ -557,7 +581,6 @@ export default class LlmChatPanel extends BasicWidget {
// Reset the UI and data // Reset the UI and data
this.noteContextChatMessages.innerHTML = ''; this.noteContextChatMessages.innerHTML = '';
this.messages = []; this.messages = [];
this.chatNoteId = null;
this.noteId = null; // Also reset the chat note ID this.noteId = null; // Also reset the chat note ID
this.hideSources(); // Hide any sources from previous note this.hideSources(); // Hide any sources from previous note
@ -569,7 +592,7 @@ export default class LlmChatPanel extends BasicWidget {
const hasSavedData = await this.loadSavedData(); const hasSavedData = await this.loadSavedData();
// Only create a new session if we don't have a session or saved data // Only create a new session if we don't have a session or saved data
if (!this.chatNoteId || !this.noteId || !hasSavedData) { if (!this.noteId || !hasSavedData) {
// Create a new chat session // Create a new chat session
await this.createChatSession(); await this.createChatSession();
} }
@ -580,19 +603,15 @@ export default class LlmChatPanel extends BasicWidget {
*/ */
private async createChatSession() { private async createChatSession() {
try { try {
// Create a new chat session, passing the current note ID if it exists // If we already have a noteId (for AI Chat notes), use it
const { chatNoteId, noteId } = await createChatSession( const contextNoteId = this.noteId || this.currentNoteId;
this.currentNoteId ? this.currentNoteId : undefined
);
if (chatNoteId) { // Create a new chat session, passing the context note ID
// If we got back an ID from the API, use it const noteId = await createChatSession(contextNoteId ? contextNoteId : undefined);
this.chatNoteId = chatNoteId;
// For new sessions, the noteId should equal the chatNoteId
// This ensures we're using the note ID consistently
this.noteId = noteId || chatNoteId;
if (noteId) {
// Set the note ID for this chat
this.noteId = noteId;
console.log(`Created new chat session with noteId: ${this.noteId}`); console.log(`Created new chat session with noteId: ${this.noteId}`);
} else { } else {
throw new Error("Failed to create chat session - no ID returned"); throw new Error("Failed to create chat session - no ID returned");
@ -645,7 +664,7 @@ export default class LlmChatPanel extends BasicWidget {
const showThinking = this.showThinkingCheckbox.checked; const showThinking = this.showThinkingCheckbox.checked;
// Add logging to verify parameters // Add logging to verify parameters
console.log(`Sending message with: useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, noteId=${this.currentNoteId}, sessionId=${this.chatNoteId}`); console.log(`Sending message with: useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, noteId=${this.currentNoteId}, sessionId=${this.noteId}`);
// Create the message parameters // Create the message parameters
const messageParams = { const messageParams = {
@ -695,11 +714,11 @@ export default class LlmChatPanel extends BasicWidget {
await validateEmbeddingProviders(this.validationWarning); await validateEmbeddingProviders(this.validationWarning);
// Make sure we have a valid session // Make sure we have a valid session
if (!this.chatNoteId) { if (!this.noteId) {
// If no session ID, create a new session // If no session ID, create a new session
await this.createChatSession(); await this.createChatSession();
if (!this.chatNoteId) { if (!this.noteId) {
// If still no session ID, show error and return // If still no session ID, show error and return
console.error("Failed to create chat session"); console.error("Failed to create chat session");
toastService.showError("Failed to create chat session"); toastService.showError("Failed to create chat session");
@ -730,7 +749,7 @@ export default class LlmChatPanel extends BasicWidget {
await this.saveCurrentData(); await this.saveCurrentData();
// Add logging to verify parameters // Add logging to verify parameters
console.log(`Sending message with: useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, noteId=${this.currentNoteId}, sessionId=${this.chatNoteId}`); console.log(`Sending message with: useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, noteId=${this.currentNoteId}, sessionId=${this.noteId}`);
// Create the message parameters // Create the message parameters
const messageParams = { const messageParams = {
@ -767,12 +786,12 @@ export default class LlmChatPanel extends BasicWidget {
*/ */
private async handleDirectResponse(messageParams: any): Promise<boolean> { private async handleDirectResponse(messageParams: any): Promise<boolean> {
try { try {
if (!this.chatNoteId) return false; if (!this.noteId) return false;
console.log(`Getting direct response using sessionId: ${this.chatNoteId} (noteId: ${this.noteId})`); console.log(`Getting direct response using sessionId: ${this.noteId} (noteId: ${this.noteId})`);
// Get a direct response from the server // Get a direct response from the server
const postResponse = await getDirectResponse(this.chatNoteId, messageParams); const postResponse = await getDirectResponse(this.noteId, messageParams);
// If the POST request returned content directly, display it // If the POST request returned content directly, display it
if (postResponse && postResponse.content) { if (postResponse && postResponse.content) {
@ -845,11 +864,11 @@ export default class LlmChatPanel extends BasicWidget {
* Set up streaming response via WebSocket * Set up streaming response via WebSocket
*/ */
private async setupStreamingResponse(messageParams: any): Promise<void> { private async setupStreamingResponse(messageParams: any): Promise<void> {
if (!this.chatNoteId) { if (!this.noteId) {
throw new Error("No session ID available"); throw new Error("No session ID available");
} }
console.log(`Setting up streaming response using sessionId: ${this.chatNoteId} (noteId: ${this.noteId})`); console.log(`Setting up streaming response using sessionId: ${this.noteId} (noteId: ${this.noteId})`);
// Store tool executions captured during streaming // Store tool executions captured during streaming
const toolExecutionsCache: Array<{ const toolExecutionsCache: Array<{
@ -862,7 +881,7 @@ export default class LlmChatPanel extends BasicWidget {
}> = []; }> = [];
return setupStreamingResponse( return setupStreamingResponse(
this.chatNoteId, this.noteId,
messageParams, messageParams,
// Content update handler // Content update handler
(content: string, isDone: boolean = false) => { (content: string, isDone: boolean = false) => {
@ -898,7 +917,7 @@ export default class LlmChatPanel extends BasicWidget {
similarity?: number; similarity?: number;
content?: string; content?: string;
}>; }>;
}>(`llm/chat/${this.chatNoteId}`) }>(`llm/chat/${this.noteId}`)
.then((sessionData) => { .then((sessionData) => {
console.log("Got updated session data:", sessionData); console.log("Got updated session data:", sessionData);

View File

@ -11,7 +11,7 @@ export interface ChatResponse {
export interface SessionResponse { export interface SessionResponse {
id: string; id: string;
title: string; title: string;
noteId?: string; noteId: string; // The ID of the chat note
} }
export interface ToolExecutionStep { export interface ToolExecutionStep {
@ -33,8 +33,8 @@ export interface MessageData {
export interface ChatData { export interface ChatData {
messages: MessageData[]; messages: MessageData[];
chatNoteId: string | null; noteId: string; // The ID of the chat note
noteId?: string | null; chatNoteId?: string; // Deprecated - kept for backward compatibility, should equal noteId
toolSteps: ToolExecutionStep[]; toolSteps: ToolExecutionStep[];
sources?: Array<{ sources?: Array<{
noteId: string; noteId: string;

View File

@ -94,6 +94,11 @@ export default class AiChatTypeWidget extends TypeWidget {
this.llmChatPanel.clearNoteContextChatMessages(); this.llmChatPanel.clearNoteContextChatMessages();
this.llmChatPanel.setMessages([]); this.llmChatPanel.setMessages([]);
// Set the note ID for the chat panel
if (note) {
this.llmChatPanel.setNoteId(note.noteId);
}
// This will load saved data via the getData callback // This will load saved data via the getData callback
await this.llmChatPanel.refresh(); await this.llmChatPanel.refresh();
this.isInitialized = true; this.isInitialized = true;
@ -130,7 +135,7 @@ export default class AiChatTypeWidget extends TypeWidget {
// Reset the chat panel UI // Reset the chat panel UI
this.llmChatPanel.clearNoteContextChatMessages(); this.llmChatPanel.clearNoteContextChatMessages();
this.llmChatPanel.setMessages([]); this.llmChatPanel.setMessages([]);
this.llmChatPanel.setChatNoteId(null); this.llmChatPanel.setNoteId(this.note.noteId);
} }
// Call the parent method to refresh // Call the parent method to refresh
@ -152,6 +157,7 @@ export default class AiChatTypeWidget extends TypeWidget {
// Make sure the chat panel has the current note ID // Make sure the chat panel has the current note ID
if (this.note) { if (this.note) {
this.llmChatPanel.setCurrentNoteId(this.note.noteId); this.llmChatPanel.setCurrentNoteId(this.note.noteId);
this.llmChatPanel.setNoteId(this.note.noteId);
} }
this.initPromise = (async () => { this.initPromise = (async () => {
@ -186,7 +192,7 @@ export default class AiChatTypeWidget extends TypeWidget {
// Format the data properly - this is the canonical format of the data // Format the data properly - this is the canonical format of the data
const formattedData = { const formattedData = {
messages: data.messages || [], messages: data.messages || [],
chatNoteId: data.chatNoteId || this.note.noteId, noteId: this.note.noteId, // Always use the note's own ID
toolSteps: data.toolSteps || [], toolSteps: data.toolSteps || [],
sources: data.sources || [], sources: data.sources || [],
metadata: { metadata: {

View File

@ -814,10 +814,11 @@ async function streamMessage(req: Request, res: Response) {
throw new Error('Content cannot be empty'); throw new Error('Content cannot be empty');
} }
// Check if session exists // Get or create session from Chat Note
const session = restChatService.getSessions().get(chatNoteId); // This will check the sessions store first, and if not found, create from the Chat Note
const session = await restChatService.getOrCreateSessionFromChatNote(chatNoteId, true);
if (!session) { if (!session) {
throw new Error('Chat not found'); throw new Error('Chat not found and could not be created from note');
} }
// Update last active timestamp // Update last active timestamp

View File

@ -480,27 +480,56 @@ class RestChatService {
const options: any = req.body || {}; const options: any = req.body || {};
const title = options.title || 'Chat Session'; const title = options.title || 'Chat Session';
// Use the currentNoteId as the chatNoteId if provided // Determine the note ID for the chat
let chatNoteId = options.chatNoteId; let noteId = options.noteId || options.chatNoteId; // Accept either name for backward compatibility
// If currentNoteId is provided but chatNoteId is not, use currentNoteId // If currentNoteId is provided, check if it's already an AI Chat note
if (!chatNoteId && options.currentNoteId) { if (!noteId && options.currentNoteId) {
chatNoteId = options.currentNoteId; // Import becca to check note type
log.info(`Using provided currentNoteId ${chatNoteId} as chatNoteId`); const becca = (await import('../../../becca/becca.js')).default;
const note = becca.notes[options.currentNoteId];
// Check if this is an AI Chat note by looking at its content structure
if (note) {
try {
const content = note.getContent();
if (content) {
const contentStr = typeof content === 'string' ? content : content.toString();
const parsedContent = JSON.parse(contentStr);
// AI Chat notes have a messages array and noteId in their content
if (parsedContent.messages && Array.isArray(parsedContent.messages) && parsedContent.noteId) {
// This looks like an AI Chat note - use it directly
noteId = options.currentNoteId;
log.info(`Using existing AI Chat note ${noteId} as session`);
}
}
} catch (e) {
// Not JSON content, so not an AI Chat note
}
}
if (!noteId) {
log.info(`Creating new chat note from context of note ${options.currentNoteId}`);
// Don't use the currentNoteId as the chat note ID - create a new one
}
} }
// If we still don't have a chatNoteId, create a new Chat Note // If we don't have a noteId, create a new Chat Note
if (!chatNoteId) { if (!noteId) {
// Create a new Chat Note via the storage service // Create a new Chat Note via the storage service
const chatStorageService = (await import('../../llm/chat_storage_service.js')).default; const chatStorageService = (await import('../../llm/chat_storage_service.js')).default;
const newChat = await chatStorageService.createChat(title); const newChat = await chatStorageService.createChat(title);
chatNoteId = newChat.id; noteId = newChat.id;
log.info(`Created new Chat Note with ID: ${chatNoteId}`); log.info(`Created new Chat Note with ID: ${noteId}`);
} else {
// We have a noteId - this means we're working with an existing aiChat note
// Don't create another note, just use the existing one
log.info(`Using existing Chat Note with ID: ${noteId}`);
} }
// Create a new session through our session store // Create a new session through our session store using the note ID
const session = SessionsStore.createSession({ const session = SessionsStore.createSession({
chatNoteId, chatNoteId: noteId, // This is really the noteId of the chat note
title, title,
systemPrompt: options.systemPrompt, systemPrompt: options.systemPrompt,
contextNoteId: options.contextNoteId, contextNoteId: options.contextNoteId,
@ -511,10 +540,10 @@ class RestChatService {
}); });
return { return {
id: session.id, id: session.id, // This will be the same as noteId
title: session.title, title: session.title,
createdAt: session.createdAt, createdAt: session.createdAt,
noteId: chatNoteId // Return the note ID explicitly noteId: noteId // Return the note ID for clarity
}; };
} catch (error: any) { } catch (error: any) {
log.error(`Error creating LLM session: ${error.message || 'Unknown error'}`); log.error(`Error creating LLM session: ${error.message || 'Unknown error'}`);