scope FableLoom reformat to one episode per request so a large loom cannot time out - #4814
Merged
Conversation
…annot time out (#4794) Rewriting a loom's scenes into the other format ran every provider call behind a single held HTTP request — 227 seconds on a 13-scene loom, and tens of minutes on a large one, long enough for a proxy or fetch timeout to kill the response while the server kept writing chunks to the record. The endpoint is now episode-scoped (POST /:id/episodes/:episodeId/reformat), and each request is capped at 20 scenes: a run that stops at that ceiling with scenes it never sent answers `capped: true`, and the settings drawer asks the same episode again. The drawer walks the episodes that still have work and names the one in flight, so a multi-minute pass shows where it is instead of spinning "Rewriting…". Two properties are unchanged on purpose: - Resumability. Each chunk of 5 scenes is still persisted as it lands and stamped with the format it was written in, so a failure keeps what already succeeded and re-running rewrites only what is left. - No half-and-half loom. The format pin stays the SERVER's call and lands only once no episode has an unconverted scene left — a browser closed mid-walk can't leave the loom claiming a format half its story isn't in, which a client-side final PATCH could not guarantee. The rewrite button now counts (and offers itself for) only the scenes not already in the target format, so a loom that is fully converted no longer offers a pass that would do nothing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rewriting a loom's scenes into the other format ran every provider call behind a single held HTTP request — 227s on a 13-scene loom, and tens of minutes on a large one, long enough for a proxy or fetch timeout to kill the response while the server kept writing chunks to the record.
POST /api/fableloom/:id/episodes/:episodeId/reformatreplaces the loom-widePOST /:id/reformat. Each request is additionally capped at 20 scenes; a run that stops at that ceiling with scenes it never sent answerscapped: true.docs/features/fableloom.md("Scene format") describes the new transport.Option A from the issue. Option B (start + SSE + cancel, the Writers Room polish shape) stays available if reformat later grows a per-scene retry or starts rewriting image prompts too; nothing here blocks it.
Test plan
server/services/fableLoom/weave.test.js— reworked for the episode-scoped service, plus new coverage: stops at the ceiling and continues where it stopped (capped: true→ next request sends only the leftover chunk); does not flag a run as capped when the model, not the ceiling, left scenes behind; holds the loom pin until every episode is converted, not just the one it rewrote; is a no-op with zero provider calls on an already-converted episode; 404s on an episode not in the loom.server/routes/fableLoom.test.js— the reformat route validates the format and forwards(loomId, episodeId, input); the old loom-scoped path is gone (404).client/src/components/fableloom/LoomSettingsDrawer.test.jsx(new) — walks episodes one request at a time, names the episode in flight, shows scenes left while re-asking a capped episode, does not re-ask an episode the model merely dropped scenes in, stops the walk on failure while still clearing the scene selection up front and re-reading afterwards, and offers no rewrite once everything is converted.client/src/components/fableloom/loomFormats.test.js— the pending-scene/pending-episode helpers.cd server && npm test(1564 files pass; the one failure,services/loras.test.js > classifies and backfills keyLayout, is an unrelated pre-existing flake that passes in isolation) andcd client && npm test(735 files, 9366 tests, all pass, noact()warnings).cd client && npm run lintclean.Closes #4794