Commit 482a528
Load full task file tree in one request, inline small contents (#998)
* perf: load task file tree and contents in one round trip
The task-definition file pane was slow because every interaction paid a
full browser -> Next.js -> backend -> S3 round trip: one request for the
root listing, one more per folder expand, and one per file click (archive-
backed tasks get no per-file presigned URLs, so content always took the
proxy path too).
- Frontend: fetch the listing recursively once on open and build the full
nested tree client-side. Folder expands are now instant; the lazy
per-prefix loader is kept only as a fallback for non-recursive
responses. The public-share auto-select now prefers instruction.md so
the nested tree doesn't default to a file inside environment/.
- Backend: archive-backed recursive listings inline small UTF-8 file
contents (100KB/file, 4MB total caps) straight from the already-loaded
tarball, so the tree and most file bodies arrive in a single response
and file clicks render with no further requests. Binary and oversize
members still go through the per-file endpoint. Inlining copies the
member dicts so the per-process archive cache stays pristine.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJwwqogxTKMCswnP8f4H7N
* perf: drop lazy directory fallback, inline all layouts, add skeleton
Follow-up to the one-round-trip listing change:
- Remove the per-directory lazy-load fallback entirely (loadDirectory,
loadingDirs, isLoaded, updateTree, buildNodesFromListing). Task trees
are shallow; the single recursive request is the only load path now,
and folder expands are pure UI state.
- Inline small file contents server-side for the expanded per-file and
plain S3 layouts too (concurrent bounded GETs next to S3), matching
the archive path, so every task listing returns the tree and the file
bodies in one response. Best-effort: binary/oversize/failed fetches
fall back to the per-file URL.
- Replace the loading spinners with a whole-pane skeleton that mirrors
the sidebar + content layout while the listing request is in flight.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJwwqogxTKMCswnP8f4H7N
* fix: clear stale loading flag when selecting a file with cached content
Switching from a file with an in-flight content fetch to one with
inlined/cached content left fileContentLoading stuck true: the cancelled
fetch skips its own reset and the cached-content early return never
cleared it, so the content pane showed the skeleton indefinitely.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJwwqogxTKMCswnP8f4H7N
* perf: stream task file listings — tree first, contents as they load
The one-shot listing blocked on the whole content fan-out before the
client saw anything. Listings can now stream NDJSON (stream=1): a
listing chunk goes out as soon as the tree is known, then per-file
content chunks follow as bodies load, shallowest files first. The panel
paints the tree off the first chunk and patches contents in behind it;
a chunk for the file currently on screen renders immediately.
- storage: parse task tarballs once (single gzip decompress) extracting
member metadata and inline-eligible text bodies in the same pass, and
cache (bytes, members, texts) per archive — warm listings and file
reads do zero decompression. This also fixes the double decompress per
listing flagged by review. stream_task_files() yields the bare tree
then contents from the cached texts (archive) or an as-completed
concurrent S3 fan-out (expanded/plain layouts).
- API: stream=1 on the authed, public-share, and self-hosted task files
endpoints returns NDJSON via a shared response helper; non-stream
responses are unchanged.
- Next.js proxies pass NDJSON bodies through instead of buffering.
- Frontend requests stream=1 and consumes the stream progressively;
endpoints that don't stream (trial files) ignore the param and the
plain-JSON path still applies.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJwwqogxTKMCswnP8f4H7N
* fix: address stream review findings
- task_expand_handler unpacked _load_task_archive's old 2-tuple; every
TASK_EXPAND job hitting an archive raised ValueError. Unpack the new
(bytes, members, texts) shape (and align the test fake, which is why
the suite missed it).
- make_task_files_ndjson_response now awaits the first chunk before
constructing the StreamingResponse, so listing-time failures (task not
found, storage errors) return real HTTP error statuses instead of
silently truncating an already-started 200 stream.
- Frontend: a stream failure after the tree has painted no longer
replaces the tree with an error state, and a failed dedicated content
fetch no longer overwrites a body the stream already delivered.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJwwqogxTKMCswnP8f4H7N
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 232199f commit 482a528
11 files changed
Lines changed: 788 additions & 203 deletions
File tree
- backend/api/routers
- frontend/src
- app/api
- public/experiments/[token]/tasks/[task_id]/files
- tasks/[task_id]/files
- components
- oddish
- src/oddish
- core/sharing
- db
- server
- workers/queue
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
| 70 | + | |
| 71 | + | |
70 | 72 | | |
71 | 73 | | |
72 | 74 | | |
| |||
1636 | 1638 | | |
1637 | 1639 | | |
1638 | 1640 | | |
1639 | | - | |
| 1641 | + | |
| 1642 | + | |
| 1643 | + | |
| 1644 | + | |
| 1645 | + | |
1640 | 1646 | | |
1641 | 1647 | | |
1642 | 1648 | | |
1643 | 1649 | | |
| 1650 | + | |
| 1651 | + | |
1644 | 1652 | | |
1645 | 1653 | | |
1646 | 1654 | | |
| |||
1654 | 1662 | | |
1655 | 1663 | | |
1656 | 1664 | | |
| 1665 | + | |
| 1666 | + | |
| 1667 | + | |
| 1668 | + | |
| 1669 | + | |
| 1670 | + | |
| 1671 | + | |
| 1672 | + | |
| 1673 | + | |
| 1674 | + | |
| 1675 | + | |
| 1676 | + | |
| 1677 | + | |
1657 | 1678 | | |
1658 | 1679 | | |
1659 | 1680 | | |
| |||
Lines changed: 15 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
23 | 37 | | |
24 | 38 | | |
25 | 39 | | |
26 | | - | |
| 40 | + | |
27 | 41 | | |
28 | 42 | | |
29 | 43 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
31 | 45 | | |
32 | | - | |
33 | | - | |
| 46 | + | |
34 | 47 | | |
35 | 48 | | |
36 | | - | |
| 49 | + | |
37 | 50 | | |
38 | 51 | | |
39 | 52 | | |
| |||
0 commit comments