fix(llm): sending messages no longer throws an error at first

This commit is contained in:
perf3ct 2025-06-03 00:18:45 +00:00
parent ab3758c9b3
commit d2ba270fdf
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232

View File

@ -809,21 +809,27 @@ async function indexNote(req: Request, res: Response) {
async function streamMessage(req: Request, res: Response) { async function streamMessage(req: Request, res: Response) {
log.info("=== Starting streamMessage ==="); log.info("=== Starting streamMessage ===");
try { try {
// Set up the response headers for streaming first, before any data is sent
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const chatNoteId = req.params.chatNoteId; const chatNoteId = req.params.chatNoteId;
const { content, useAdvancedContext, showThinking, mentions } = req.body; const { content, useAdvancedContext, showThinking, mentions } = req.body;
if (!content || typeof content !== 'string' || content.trim().length === 0) { if (!content || typeof content !== 'string' || content.trim().length === 0) {
// Early return with error return res.status(400).json({
res.write(`data: ${JSON.stringify({ error: 'Content cannot be empty', done: true })}\n\n`); success: false,
res.end(); error: 'Content cannot be empty'
return; });
} }
// IMPORTANT: Immediately send a success response to the initial POST request
// The client is waiting for this to confirm streaming has been initiated
res.status(200).json({
success: true,
message: 'Streaming initiated successfully'
});
log.info(`Sent immediate success response for streaming setup`);
// Create a new response object for streaming through WebSocket only
// We won't use HTTP streaming since we've already sent the HTTP response
// Get or create chat directly from storage (simplified approach) // Get or create chat directly from storage (simplified approach)
let chat = await chatStorageService.getChat(chatNoteId); let chat = await chatStorageService.getChat(chatNoteId);
if (!chat) { if (!chat) {
@ -883,28 +889,70 @@ async function streamMessage(req: Request, res: Response) {
thinking: showThinking ? 'Initializing streaming LLM response...' : undefined thinking: showThinking ? 'Initializing streaming LLM response...' : undefined
}); });
// Process the streaming request directly // Instead of trying to reimplement the streaming logic ourselves,
// delegate to restChatService but set up the correct protocol:
// 1. We've already sent a success response to the initial POST
// 2. Now we'll have restChatService process the actual streaming through WebSocket
try { try {
// Call the streaming handler - it will handle the response streaming // Import the WebSocket service for sending messages
// IMPORTANT: We do not await this because we don't want to try sending a response const wsService = (await import('../../services/ws.js')).default;
// after the streaming has completed - the stream handler takes care of ending the response
restChatService.handleSendMessage({
...req,
method: 'GET', // Indicate streaming mode
query: {
...req.query,
stream: 'true' // Add the required stream parameter
},
body: {
content: enhancedContent,
useAdvancedContext: useAdvancedContext === true,
showThinking: showThinking === true
},
params: { chatNoteId }
} as unknown as Request, res);
// Don't return or send any additional response here // Create a simple pass-through response object that won't write to the HTTP response
// handleSendMessage handles the full streaming response cycle and will end the response // but will allow restChatService to send WebSocket messages
const dummyResponse = {
writableEnded: false,
// Implement methods that would normally be used by restChatService
write: (_chunk: string) => {
// Silent no-op - we're only using WebSocket
return true;
},
end: (_chunk?: string) => {
// Log when streaming is complete via WebSocket
log.info(`[${chatNoteId}] Completed HTTP response handling during WebSocket streaming`);
return dummyResponse;
},
setHeader: (name: string, _value: string) => {
// Only log for content-type to reduce noise
if (name.toLowerCase() === 'content-type') {
log.info(`[${chatNoteId}] Setting up streaming for WebSocket only`);
}
return dummyResponse;
}
};
// Process the streaming now through WebSocket only
try {
log.info(`[${chatNoteId}] Processing LLM streaming through WebSocket after successful initiation at ${new Date().toISOString()}`);
// Call restChatService with our enhanced request and dummy response
// The important part is setting method to GET to indicate streaming mode
await restChatService.handleSendMessage({
...req,
method: 'GET', // Indicate streaming mode
query: {
...req.query,
stream: 'true' // Add the required stream parameter
},
body: {
content: enhancedContent,
useAdvancedContext: useAdvancedContext === true,
showThinking: showThinking === true
},
params: { chatNoteId }
} as unknown as Request, dummyResponse as unknown as Response);
log.info(`[${chatNoteId}] WebSocket streaming completed at ${new Date().toISOString()}`);
} catch (streamError) {
log.error(`[${chatNoteId}] Error during WebSocket streaming: ${streamError}`);
// Send error message through WebSocket
wsService.sendMessageToAllClients({
type: 'llm-stream',
chatNoteId: chatNoteId,
error: `Error during streaming: ${streamError}`,
done: true
});
}
} catch (error) { } catch (error) {
log.error(`Error during streaming: ${error}`); log.error(`Error during streaming: ${error}`);