decouple sessions even further

i think this is a better idea?
This commit is contained in:
perf3ct 2025-04-16 21:28:17 +00:00
parent 06924aad59
commit cdc84f1cef
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232
2 changed files with 315 additions and 287 deletions

View File

@ -45,10 +45,10 @@ interface SessionOptions {
/**
* @swagger
* /api/llm/sessions:
* /api/llm/chat:
* post:
* summary: Create a new LLM chat session
* operationId: llm-create-session
* summary: Create a new LLM chat
* operationId: llm-create-chat
* requestBody:
* required: true
* content:
@ -58,7 +58,7 @@ interface SessionOptions {
* properties:
* title:
* type: string
* description: Title for the chat session
* description: Title for the chat
* systemPrompt:
* type: string
* description: System message to set the behavior of the assistant
@ -76,16 +76,16 @@ interface SessionOptions {
* description: LLM provider to use (e.g., 'openai', 'anthropic', 'ollama')
* contextNoteId:
* type: string
* description: Note ID to use as context for the session
* description: Note ID to use as context for the chat
* responses:
* '200':
* description: Successfully created session
* description: Successfully created chat
* content:
* application/json:
* schema:
* type: object
* properties:
* sessionId:
* chatNoteId:
* type: string
* title:
* type: string
@ -96,25 +96,25 @@ interface SessionOptions {
* - session: []
* tags: ["llm"]
*/
async function createSession(req: Request, res: Response) {
async function createChat(req: Request, res: Response) {
return restChatService.createSession(req, res);
}
/**
* @swagger
* /api/llm/sessions/{sessionId}:
* /api/llm/chat/{chatNoteId}:
* get:
* summary: Retrieve a specific chat session by ID
* operationId: llm-get-session
* summary: Retrieve a specific chat by ID
* operationId: llm-get-chat
* parameters:
* - name: sessionId
* - name: chatNoteId
* in: path
* required: true
* schema:
* type: string
* responses:
* '200':
* description: Chat session details
* description: Chat details
* content:
* application/json:
* schema:
@ -144,23 +144,23 @@ async function createSession(req: Request, res: Response) {
* type: string
* format: date-time
* '404':
* description: Session not found
* description: Chat not found
* security:
* - session: []
* tags: ["llm"]
*/
async function getSession(req: Request, res: Response) {
async function getChat(req: Request, res: Response) {
return restChatService.getSession(req, res);
}
/**
* @swagger
* /api/llm/sessions/{sessionId}:
* /api/llm/chat/{chatNoteId}:
* patch:
* summary: Update a chat session's settings
* operationId: llm-update-session
* summary: Update a chat's settings
* operationId: llm-update-chat
* parameters:
* - name: sessionId
* - name: chatNoteId
* in: path
* required: true
* schema:
@ -174,7 +174,7 @@ async function getSession(req: Request, res: Response) {
* properties:
* title:
* type: string
* description: Updated title for the session
* description: Updated title for the chat
* systemPrompt:
* type: string
* description: Updated system prompt
@ -195,7 +195,7 @@ async function getSession(req: Request, res: Response) {
* description: Updated note ID for context
* responses:
* '200':
* description: Session successfully updated
* description: Chat successfully updated
* content:
* application/json:
* schema:
@ -209,46 +209,46 @@ async function getSession(req: Request, res: Response) {
* type: string
* format: date-time
* '404':
* description: Session not found
* description: Chat not found
* security:
* - session: []
* tags: ["llm"]
*/
async function updateSession(req: Request, res: Response) {
// Get the session using ChatService
const sessionId = req.params.sessionId;
async function updateChat(req: Request, res: Response) {
// Get the chat using ChatService
const chatNoteId = req.params.chatNoteId;
const updates = req.body;
try {
// Get the session
const session = await chatService.getOrCreateSession(sessionId);
// Get the chat
const session = await chatService.getOrCreateSession(chatNoteId);
// Update title if provided
if (updates.title) {
await chatStorageService.updateChat(sessionId, session.messages, updates.title);
await chatStorageService.updateChat(chatNoteId, session.messages, updates.title);
}
// Return the updated session
// Return the updated chat
return {
id: sessionId,
id: chatNoteId,
title: updates.title || session.title,
updatedAt: new Date()
};
} catch (error) {
log.error(`Error updating session: ${error}`);
throw new Error(`Failed to update session: ${error}`);
log.error(`Error updating chat: ${error}`);
throw new Error(`Failed to update chat: ${error}`);
}
}
/**
* @swagger
* /api/llm/sessions:
* /api/llm/chat:
* get:
* summary: List all chat sessions
* operationId: llm-list-sessions
* summary: List all chats
* operationId: llm-list-chats
* responses:
* '200':
* description: List of chat sessions
* description: List of chats
* content:
* application/json:
* schema:
@ -272,14 +272,14 @@ async function updateSession(req: Request, res: Response) {
* - session: []
* tags: ["llm"]
*/
async function listSessions(req: Request, res: Response) {
// Get all sessions using ChatService
async function listChats(req: Request, res: Response) {
// Get all chats using ChatService
try {
const sessions = await chatService.getAllSessions();
// Format the response
return {
sessions: sessions.map(session => ({
chats: sessions.map(session => ({
id: session.id,
title: session.title,
createdAt: new Date(), // Since we don't have this in chat sessions
@ -288,44 +288,44 @@ async function listSessions(req: Request, res: Response) {
}))
};
} catch (error) {
log.error(`Error listing sessions: ${error}`);
throw new Error(`Failed to list sessions: ${error}`);
log.error(`Error listing chats: ${error}`);
throw new Error(`Failed to list chats: ${error}`);
}
}
/**
* @swagger
* /api/llm/sessions/{sessionId}:
* /api/llm/chat/{chatNoteId}:
* delete:
* summary: Delete a chat session
* operationId: llm-delete-session
* summary: Delete a chat
* operationId: llm-delete-chat
* parameters:
* - name: sessionId
* - name: chatNoteId
* in: path
* required: true
* schema:
* type: string
* responses:
* '200':
* description: Session successfully deleted
* description: Chat successfully deleted
* '404':
* description: Session not found
* description: Chat not found
* security:
* - session: []
* tags: ["llm"]
*/
async function deleteSession(req: Request, res: Response) {
async function deleteChat(req: Request, res: Response) {
return restChatService.deleteSession(req, res);
}
/**
* @swagger
* /api/llm/sessions/{sessionId}/messages:
* /api/llm/chat/{chatNoteId}/messages:
* post:
* summary: Send a message to an LLM and get a response
* operationId: llm-send-message
* parameters:
* - name: sessionId
* - name: chatNoteId
* in: path
* required: true
* schema:
@ -357,7 +357,7 @@ async function deleteSession(req: Request, res: Response) {
* description: Whether to include relevant notes as context
* useNoteContext:
* type: boolean
* description: Whether to use the session's context note
* description: Whether to use the chat's context note
* responses:
* '200':
* description: LLM response
@ -379,10 +379,10 @@ async function deleteSession(req: Request, res: Response) {
* type: string
* similarity:
* type: number
* sessionId:
* chatNoteId:
* type: string
* '404':
* description: Session not found
* description: Chat not found
* '500':
* description: Error processing request
* security:
@ -393,6 +393,175 @@ async function sendMessage(req: Request, res: Response) {
return restChatService.handleSendMessage(req, res);
}
/**
* @swagger
* /api/llm/chat/{chatNoteId}/messages/stream:
* post:
* summary: Start a streaming response via WebSockets
* operationId: llm-stream-message
* parameters:
* - name: chatNoteId
* in: path
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* content:
* type: string
* description: The user message to send to the LLM
* useAdvancedContext:
* type: boolean
* description: Whether to use advanced context extraction
* showThinking:
* type: boolean
* description: Whether to show thinking process in the response
* responses:
* '200':
* description: Streaming started successfully
* '404':
* description: Chat not found
* '500':
* description: Error processing request
* security:
* - session: []
* tags: ["llm"]
*/
async function streamMessage(req: Request, res: Response) {
log.info("=== Starting streamMessage ===");
try {
const chatNoteId = req.params.chatNoteId;
const { content, useAdvancedContext, showThinking } = req.body;
if (!content || typeof content !== 'string' || content.trim().length === 0) {
throw new Error('Content cannot be empty');
}
// Check if session exists in memory
let session = restChatService.getSessions().get(chatNoteId);
// If session doesn't exist in memory, try to create it from the Chat Note
if (!session) {
log.info(`Session not found in memory for Chat Note ${chatNoteId}, attempting to create from Chat Note`);
const restoredSession = await restChatService.createSessionFromChatNote(chatNoteId);
if (!restoredSession) {
// If we can't find the Chat Note, then it's truly not found
log.error(`Chat Note ${chatNoteId} not found, cannot create session`);
throw new Error('Chat Note not found, cannot create session for streaming');
}
session = restoredSession;
}
// Update last active timestamp
session.lastActive = new Date();
// Add user message to the session
session.messages.push({
role: 'user',
content,
timestamp: new Date()
});
// Create request parameters for the pipeline
const requestParams = {
chatNoteId,
content,
useAdvancedContext: useAdvancedContext === true,
showThinking: showThinking === true,
stream: true // Always stream for this endpoint
};
// Create a fake request/response pair to pass to the handler
const fakeReq = {
...req,
method: 'GET', // Set to GET to indicate streaming
query: {
stream: 'true', // Set stream param - don't use format: 'stream' to avoid confusion
useAdvancedContext: String(useAdvancedContext === true),
showThinking: String(showThinking === true)
},
params: {
chatNoteId
},
// Make sure the original content is available to the handler
body: {
content,
useAdvancedContext: useAdvancedContext === true,
showThinking: showThinking === true
}
} as unknown as Request;
// Log to verify correct parameters
log.info(`WebSocket stream settings - useAdvancedContext=${useAdvancedContext === true}, in query=${fakeReq.query.useAdvancedContext}, in body=${fakeReq.body.useAdvancedContext}`);
// Extra safety to ensure the parameters are passed correctly
if (useAdvancedContext === true) {
log.info(`Enhanced context IS enabled for this request`);
} else {
log.info(`Enhanced context is NOT enabled for this request`);
}
// Process the request in the background
Promise.resolve().then(async () => {
try {
await restChatService.handleSendMessage(fakeReq, res);
} catch (error) {
log.error(`Background message processing error: ${error}`);
// Import the WebSocket service
const wsService = (await import('../../services/ws.js')).default;
// Define LLMStreamMessage interface
interface LLMStreamMessage {
type: 'llm-stream';
sessionId: string; // Keep this as sessionId for WebSocket compatibility
content?: string;
thinking?: string;
toolExecution?: any;
done?: boolean;
error?: string;
raw?: unknown;
}
// Send error to client via WebSocket
wsService.sendMessageToAllClients({
type: 'llm-stream',
sessionId: chatNoteId, // Use sessionId property, but pass the chatNoteId
error: `Error processing message: ${error}`,
done: true
} as LLMStreamMessage);
}
});
// Import the WebSocket service
const wsService = (await import('../../services/ws.js')).default;
// Let the client know streaming has started via WebSocket (helps client confirm connection is working)
wsService.sendMessageToAllClients({
type: 'llm-stream',
sessionId: chatNoteId,
thinking: 'Initializing streaming LLM response...'
});
// Let the client know streaming has started via HTTP response
return {
success: true,
message: 'Streaming started',
sessionId: chatNoteId // Keep using sessionId for API response compatibility
};
} catch (error: any) {
log.error(`Error starting message stream: ${error.message}`);
throw error;
}
}
/**
* @swagger
* /api/llm/indexes/stats:
@ -788,182 +957,13 @@ async function indexNote(req: Request, res: Response) {
}
}
/**
* @swagger
* /api/llm/sessions/{sessionId}/messages/stream:
* post:
* summary: Start a streaming response session via WebSockets
* operationId: llm-stream-message
* parameters:
* - name: sessionId
* in: path
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* content:
* type: string
* description: The user message to send to the LLM
* useAdvancedContext:
* type: boolean
* description: Whether to use advanced context extraction
* showThinking:
* type: boolean
* description: Whether to show thinking process in the response
* responses:
* '200':
* description: Streaming started successfully
* '404':
* description: Session not found
* '500':
* description: Error processing request
* security:
* - session: []
* tags: ["llm"]
*/
async function streamMessage(req: Request, res: Response) {
log.info("=== Starting streamMessage ===");
try {
const sessionId = req.params.sessionId;
const { content, useAdvancedContext, showThinking } = req.body;
if (!content || typeof content !== 'string' || content.trim().length === 0) {
throw new Error('Content cannot be empty');
}
// Check if session exists in memory
let session = restChatService.getSessions().get(sessionId);
// If session doesn't exist in memory, try to recreate it from the Chat Note
if (!session) {
log.info(`Session ${sessionId} not found in memory, attempting to restore from Chat Note`);
const restoredSession = await restChatService.restoreSessionFromChatNote(sessionId);
if (!restoredSession) {
// If we can't find the Chat Note either, then it's truly not found
log.error(`Chat Note ${sessionId} not found, cannot restore session`);
throw new Error('Session not found and no corresponding Chat Note exists');
}
session = restoredSession;
}
// Update last active timestamp
session.lastActive = new Date();
// Add user message to the session
session.messages.push({
role: 'user',
content,
timestamp: new Date()
});
// Create request parameters for the pipeline
const requestParams = {
sessionId,
content,
useAdvancedContext: useAdvancedContext === true,
showThinking: showThinking === true,
stream: true // Always stream for this endpoint
};
// Create a fake request/response pair to pass to the handler
const fakeReq = {
...req,
method: 'GET', // Set to GET to indicate streaming
query: {
stream: 'true', // Set stream param - don't use format: 'stream' to avoid confusion
useAdvancedContext: String(useAdvancedContext === true),
showThinking: String(showThinking === true)
},
params: {
sessionId
},
// Make sure the original content is available to the handler
body: {
content,
useAdvancedContext: useAdvancedContext === true,
showThinking: showThinking === true
}
} as unknown as Request;
// Log to verify correct parameters
log.info(`WebSocket stream settings - useAdvancedContext=${useAdvancedContext === true}, in query=${fakeReq.query.useAdvancedContext}, in body=${fakeReq.body.useAdvancedContext}`);
// Extra safety to ensure the parameters are passed correctly
if (useAdvancedContext === true) {
log.info(`Enhanced context IS enabled for this request`);
} else {
log.info(`Enhanced context is NOT enabled for this request`);
}
// Process the request in the background
Promise.resolve().then(async () => {
try {
await restChatService.handleSendMessage(fakeReq, res);
} catch (error) {
log.error(`Background message processing error: ${error}`);
// Import the WebSocket service
const wsService = (await import('../../services/ws.js')).default;
// Define LLMStreamMessage interface
interface LLMStreamMessage {
type: 'llm-stream';
sessionId: string;
content?: string;
thinking?: string;
toolExecution?: any;
done?: boolean;
error?: string;
raw?: unknown;
}
// Send error to client via WebSocket
wsService.sendMessageToAllClients({
type: 'llm-stream',
sessionId,
error: `Error processing message: ${error}`,
done: true
} as LLMStreamMessage);
}
});
// Import the WebSocket service
const wsService = (await import('../../services/ws.js')).default;
// Let the client know streaming has started via WebSocket (helps client confirm connection is working)
wsService.sendMessageToAllClients({
type: 'llm-stream',
sessionId,
thinking: 'Initializing streaming LLM response...'
});
// Let the client know streaming has started via HTTP response
return {
success: true,
message: 'Streaming started',
sessionId
};
} catch (error: any) {
log.error(`Error starting message stream: ${error.message}`);
throw error;
}
}
export default {
// Chat session management
createSession,
getSession,
updateSession,
listSessions,
deleteSession,
// Chat management
createChat,
getChat,
updateChat,
listChats,
deleteChat,
sendMessage,
streamMessage,

View File

@ -84,7 +84,7 @@ class RestChatService {
log.info("=== Starting handleSendMessage ===");
try {
// Extract parameters differently based on the request method
let content, useAdvancedContext, showThinking, sessionId;
let content, useAdvancedContext, showThinking, chatNoteId;
if (req.method === 'POST') {
// For POST requests, get content from the request body
@ -94,7 +94,7 @@ class RestChatService {
showThinking = requestBody.showThinking || false;
// Add logging for POST requests
log.info(`LLM POST message: sessionId=${req.params.sessionId}, useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, contentLength=${content ? content.length : 0}`);
log.info(`LLM POST message: chatNoteId=${req.params.chatNoteId}, useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, contentLength=${content ? content.length : 0}`);
} else if (req.method === 'GET') {
// For GET (streaming) requests, get parameters from query params and body
// For streaming requests, we need the content from the body
@ -103,13 +103,13 @@ class RestChatService {
content = req.body && req.body.content ? req.body.content : '';
// Add detailed logging for GET requests
log.info(`LLM GET stream: sessionId=${req.params.sessionId}, useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}`);
log.info(`LLM GET stream: chatNoteId=${req.params.chatNoteId}, useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}`);
log.info(`Parameters from query: useAdvancedContext=${req.query.useAdvancedContext}, showThinking=${req.query.showThinking}`);
log.info(`Parameters from body: useAdvancedContext=${req.body?.useAdvancedContext}, showThinking=${req.body?.showThinking}, content=${content ? `${content.substring(0, 20)}...` : 'none'}`);
}
// Get sessionId from URL params since it's part of the route
sessionId = req.params.sessionId;
// Get chatNoteId from URL params since it's part of the route
chatNoteId = req.params.chatNoteId || req.params.sessionId; // Support both names for backward compatibility
// For GET requests, ensure we have the stream parameter
if (req.method === 'GET' && req.query.stream !== 'true') {
@ -121,46 +121,48 @@ class RestChatService {
throw new Error('Content cannot be empty');
}
// Check if session exists, create one if not
let session = SessionsStore.getSession(sessionId);
// Get or create session from Chat Note
let session = await this.getOrCreateSessionFromChatNote(chatNoteId, req.method === 'POST');
// If no session found and we're not allowed to create one (GET request)
if (!session && req.method === 'GET') {
throw new Error('Chat Note not found, cannot create session for streaming');
}
// For POST requests, if no Chat Note exists, create a new one
if (!session && req.method === 'POST') {
log.info(`No Chat Note found for ${chatNoteId}, creating a new Chat Note and session`);
// Create a new Chat Note via the storage service
const chatStorageService = (await import('../../llm/chat_storage_service.js')).default;
const newChat = await chatStorageService.createChat('New Chat');
// Use the new Chat Note's ID for the session
session = SessionsStore.createSession({
title: newChat.title
});
// Update the session ID to match the Chat Note ID
session.id = newChat.id;
log.info(`Created new Chat Note and session with ID: ${session.id}`);
// Update the parameter to use the new ID
chatNoteId = session.id;
}
// At this point, session should never be null
// TypeScript doesn't know this, so we'll add a check
if (!session) {
if (req.method === 'GET') {
// For GET requests, we should try to restore from Chat Note
log.info(`Session ${sessionId} not found in memory for GET request, attempting to restore from Chat Note`);
const restoredSession = await this.restoreSessionFromChatNote(sessionId);
if (!restoredSession) {
// If we still can't find the Chat Note, throw error
throw new Error('Session not found and no corresponding Chat Note exists');
}
session = restoredSession;
} else {
// For POST requests, we can create a new session, or try to restore from Chat Note
log.info(`Session ${sessionId} not found for POST request, checking if Chat Note exists`);
const restoredSession = await this.restoreSessionFromChatNote(sessionId);
if (!restoredSession) {
// If we can't find a Chat Note, create a new session
log.info(`No Chat Note found for ${sessionId}, creating a new session`);
session = SessionsStore.createSession({
title: 'Auto-created Session'
});
log.info(`Created new session with ID: ${session.id}`);
} else {
session = restoredSession;
}
}
// This should never happen due to our logic above
throw new Error('Failed to create or retrieve session');
}
// Update session last active timestamp
SessionsStore.touchSession(session.id);
// For POST requests, store the user message
if (req.method === 'POST' && content) {
if (req.method === 'POST' && content && session) {
// Add message to session
session.messages.push({
role: 'user',
@ -261,15 +263,15 @@ class RestChatService {
const pipelineOptions = {
// Force useAdvancedContext to be a boolean, no matter what
useAdvancedContext: useAdvancedContext === true,
systemPrompt: session.messages.find(m => m.role === 'system')?.content,
temperature: session.metadata.temperature,
maxTokens: session.metadata.maxTokens,
model: session.metadata.model,
systemPrompt: session?.messages.find(m => m.role === 'system')?.content,
temperature: session?.metadata.temperature,
maxTokens: session?.metadata.maxTokens,
model: session?.metadata.model,
// Set stream based on request type, but ensure it's explicitly a boolean value
// GET requests or format=stream parameter indicates streaming should be used
stream: !!(req.method === 'GET' || req.query.format === 'stream' || req.query.stream === 'true'),
// Include sessionId for tracking tool executions
sessionId: sessionId
// Include chatNoteId for tracking tool executions
sessionId: chatNoteId // Use sessionId property for backward compatibility
};
// Log the options to verify what's being sent to the pipeline
@ -300,7 +302,7 @@ class RestChatService {
// Use WebSocket service to send messages
this.handleStreamCallback(
data, done, rawChunk,
wsService.default, sessionId,
wsService.default, chatNoteId,
messageContent, session, res
);
} catch (error) {
@ -310,7 +312,7 @@ class RestChatService {
try {
wsService.default.sendMessageToAllClients({
type: 'llm-stream',
sessionId,
sessionId: chatNoteId, // Use sessionId property for backward compatibility
error: `Stream error: ${error instanceof Error ? error.message : 'Unknown error'}`,
done: true
});
@ -379,7 +381,7 @@ class RestChatService {
done: boolean,
rawChunk: any,
wsService: any,
sessionId: string,
chatNoteId: string,
messageContent: string,
session: any,
res: Response
@ -392,7 +394,7 @@ class RestChatService {
// Create a message object with all necessary fields
const message: LLMStreamMessage = {
type: 'llm-stream',
sessionId
sessionId: chatNoteId // Use sessionId property for backward compatibility
};
// Add content if available - either the new chunk or full content on completion
@ -445,7 +447,7 @@ class RestChatService {
// Log what was sent (first message and completion)
if (message.thinking || done) {
log.info(
`[WS-SERVER] Sending LLM stream message: sessionId=${sessionId}, content=${!!message.content}, contentLength=${message.content?.length || 0}, thinking=${!!message.thinking}, toolExecution=${!!message.toolExecution}, done=${done}`
`[WS-SERVER] Sending LLM stream message: chatNoteId=${chatNoteId}, content=${!!message.content}, contentLength=${message.content?.length || 0}, thinking=${!!message.thinking}, toolExecution=${!!message.toolExecution}, done=${done}`
);
}
@ -577,25 +579,25 @@ class RestChatService {
}
/**
* Restore a session from a Chat Note
* This is used when a session doesn't exist in memory but there's a corresponding Chat Note
* Create an in-memory session from a Chat Note
* This treats the Chat Note as the source of truth, using its ID as the session ID
*/
async restoreSessionFromChatNote(sessionId: string): Promise<ChatSession | null> {
async createSessionFromChatNote(noteId: string): Promise<ChatSession | null> {
try {
log.info(`Attempting to restore session ${sessionId} from Chat Note`);
log.info(`Creating in-memory session for Chat Note ID ${noteId}`);
// Import chat storage service
const chatStorageService = (await import('../../llm/chat_storage_service.js')).default;
// Try to get the Chat Note data
const chatNote = await chatStorageService.getChat(sessionId);
const chatNote = await chatStorageService.getChat(noteId);
if (!chatNote) {
log.error(`Chat Note ${sessionId} not found, cannot restore session`);
log.error(`Chat Note ${noteId} not found, cannot create session`);
return null;
}
log.info(`Found Chat Note ${sessionId}, recreating session from it`);
log.info(`Found Chat Note ${noteId}, creating in-memory session`);
// Convert Message[] to ChatMessage[] by ensuring the role is compatible
const chatMessages: ChatMessage[] = chatNote.messages.map(msg => ({
@ -606,7 +608,7 @@ class RestChatService {
// Create a new session with the same ID as the Chat Note
const session: ChatSession = {
id: chatNote.id,
id: chatNote.id, // Use Chat Note ID as the session ID
title: chatNote.title,
messages: chatMessages,
createdAt: chatNote.createdAt || new Date(),
@ -615,15 +617,41 @@ class RestChatService {
};
// Add the session to the in-memory store
SessionsStore.getAllSessions().set(sessionId, session);
SessionsStore.getAllSessions().set(noteId, session);
log.info(`Successfully restored session ${sessionId} from Chat Note`);
log.info(`Successfully created in-memory session for Chat Note ${noteId}`);
return session;
} catch (error) {
log.error(`Failed to restore session from Chat Note: ${error}`);
log.error(`Failed to create session from Chat Note: ${error}`);
return null;
}
}
/**
* Get an existing session or create a new one from a Chat Note
* This treats the Chat Note as the source of truth, using its ID as the session ID
*/
async getOrCreateSessionFromChatNote(noteId: string, createIfNotFound: boolean = true): Promise<ChatSession | null> {
// First check if we already have this session in memory
let session = SessionsStore.getSession(noteId);
if (session) {
log.info(`Found existing in-memory session for Chat Note ${noteId}`);
return session;
}
// If not in memory, try to create from Chat Note
log.info(`Session not found in memory for Chat Note ${noteId}, attempting to create it`);
// Only try to create if allowed
if (!createIfNotFound) {
log.info(`Not creating new session for ${noteId} as createIfNotFound=false`);
return null;
}
// Create from Chat Note
return await this.createSessionFromChatNote(noteId);
}
}
// Create singleton instance