- Fix critical await bug in extract-stream endpoint - Add comprehensive logging to LLM and parser modules - Implement fallback to standard completion for incompatible models - Create enhanced v2.0 prompts with social media handling and few-shot examples - Add LLM health check endpoint - Decompose share page into 6 focused Svelte 5 snippets Resolves LM Studio integration issues and improves code maintainability
85 lines
2.3 KiB
TypeScript
85 lines
2.3 KiB
TypeScript
/**
|
|
* Server-Sent Events (SSE) endpoint for real-time extraction progress
|
|
*
|
|
* This endpoint streams extraction progress updates to the frontend
|
|
* using the SSE protocol. Each event contains status updates, method attempts,
|
|
* retry information, and final results.
|
|
*/
|
|
|
|
import { json, type RequestHandler } from '@sveltejs/kit';
|
|
import { extractTextAndThumbnail, type ProgressEvent } from '$lib/server/extraction';
|
|
import { extractRecipe } from '$lib/server/parser';
|
|
|
|
export const POST: RequestHandler = async ({ request }) => {
|
|
const { url } = await request.json();
|
|
|
|
if (!url) {
|
|
return json({ error: 'URL is required' }, { status: 400 });
|
|
}
|
|
|
|
// Create a ReadableStream for SSE
|
|
const stream = new ReadableStream({
|
|
async start(controller) {
|
|
const encoder = new TextEncoder();
|
|
|
|
// Helper to send SSE message
|
|
const sendEvent = (event: ProgressEvent) => {
|
|
const data = JSON.stringify(event);
|
|
const message = `event: progress\ndata: ${data}\n\n`;
|
|
controller.enqueue(encoder.encode(message));
|
|
};
|
|
|
|
try {
|
|
// Extract with progress callback
|
|
const extracted = await extractTextAndThumbnail(url, sendEvent);
|
|
|
|
// Parse recipe from extracted text
|
|
sendEvent({
|
|
type: 'status',
|
|
message: 'Parsing recipe...',
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
const recipe = await extractRecipe(extracted.bodyText);
|
|
|
|
// Send final result
|
|
const completeEvent: ProgressEvent = {
|
|
type: 'complete',
|
|
message: 'Extraction and parsing completed',
|
|
data: {
|
|
recipe,
|
|
thumbnail: extracted.thumbnail
|
|
},
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
const completeMessage = `event: complete\ndata: ${JSON.stringify(completeEvent)}\n\n`;
|
|
controller.enqueue(encoder.encode(completeMessage));
|
|
|
|
controller.close();
|
|
} catch (error) {
|
|
// Send error event
|
|
const errorEvent: ProgressEvent = {
|
|
type: 'error',
|
|
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
const errorMessage = `event: error\ndata: ${JSON.stringify(errorEvent)}\n\n`;
|
|
controller.enqueue(encoder.encode(errorMessage));
|
|
|
|
controller.close();
|
|
}
|
|
}
|
|
});
|
|
|
|
// Return SSE response
|
|
return new Response(stream, {
|
|
headers: {
|
|
'Content-Type': 'text/event-stream',
|
|
'Cache-Control': 'no-cache',
|
|
Connection: 'keep-alive'
|
|
}
|
|
});
|
|
};
|