feat: stream chapter-name generation via SSE batching
All checks were successful
Release / Test backend (push) Successful in 51s
Release / Check ui (push) Successful in 47s
Release / Docker / caddy (push) Successful in 44s
Release / Docker / backend (push) Successful in 3m0s
Release / Docker / runner (push) Successful in 2m38s
Release / Docker / ui (push) Successful in 2m16s
Release / Gitea Release (push) Successful in 36s

Split chapter-name LLM requests into 100-chapter batches and stream
results back as SSE so large books (e.g. Shadow Slave: 2916 chapters)
never time out or truncate. Frontend shows live batch progress inline
and accumulates proposals as they arrive.
This commit is contained in:
Admin
2026-04-05 00:32:18 +05:00
parent b0d8c02787
commit ad2d1a2603
6 changed files with 660 additions and 178 deletions

View File

@@ -2,10 +2,11 @@
* POST /api/admin/text-gen/chapter-names
*
* Admin-only proxy to the Go backend's chapter-name generation endpoint.
* Returns AI-proposed chapter titles; does NOT persist anything.
* The backend streams SSE events; this handler pipes the stream through
* directly so the browser can consume it without buffering.
*/
import { json, error } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { log } from '$lib/server/logger';
import { backendFetch } from '$lib/server/scraper';
@@ -28,6 +29,22 @@ export const POST: RequestHandler = async ({ request, locals }) => {
throw error(502, 'Could not reach backend');
}
const data = await res.json().catch(() => ({}));
return json(data, { status: res.status });
// Non-2xx: the backend returned a JSON error before switching to SSE.
if (!res.ok) {
const data = await res.json().catch(() => ({}));
return new Response(JSON.stringify(data), {
status: res.status,
headers: { 'Content-Type': 'application/json' }
});
}
// Pipe the SSE stream straight through — do not buffer.
return new Response(res.body, {
status: 200,
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no'
}
});
};