mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 11:02:28 +08:00
fix(llm): sending messages no longer throws an error at first
This commit is contained in:
parent
ab3758c9b3
commit
d2ba270fdf
@ -809,20 +809,26 @@ 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);
|
||||||
@ -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}`);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user