Commit 5dc8759
authored
feat: lazy loading for Genie conversations (#163)
* feat: lazy loading for Genie conversations
Load only the most recent page of messages when opening a conversation,
and fetch older messages on demand when the user scrolls to the top.
- Add `history_info` SSE event with pagination token
- Add REST endpoint GET /:alias/conversations/:conversationId/messages
- Frontend auto-triggers loading via IntersectionObserver with scroll
position preservation
- Consolidate to single `listConversationMessages` method in connector
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* feat: lazy loading for Genie conversations
Load only the most recent page of messages when opening a conversation,
and fetch older messages on demand when the user scrolls to the top.
- Add `history_info` SSE event with pagination token to existing
getConversation endpoint (no new endpoints)
- Frontend auto-triggers loading via IntersectionObserver with scroll
position preservation using the same SSE endpoint with pageToken
- Consolidate to single `listConversationMessages` method in connector
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* refactor: extract fetchConversationPage and fix review issues
- Extract shared fetchConversationPage for loadHistory and loadOlderMessages
- Rename processEvent to processStreamEvent (only used for sendMessage)
- Pass stable React setters directly instead of recreating paginationCallbacks
- Add abort signal to loadOlderMessages to prevent leaks on unmount
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* fix: use genieConnectorDefaults.initialPageSize in tests
Reference the constant directly instead of hardcoding the value,
preventing test failures when the default is changed.
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* refactor: extract scroll hooks from GenieChatMessageList
Extract useScrollManagement and useLoadOlderOnScroll into custom hooks,
inline StreamingIndicator, leaving the component as clean JSX.
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* refactor: minor cleanup in GenieChatMessageList
- useLoadOlderOnScroll owns its sentinel ref internally
- Simplify message rendering with filter+map
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* refactor: simplify useGenieChat internals
- Merge ref-sync effects into one
- Extract fetchPage helper shared by loadHistory and loadOlderMessages
- Simplify query_result handler with .map() instead of imperative loop
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* fix: address PR review feedback
- Rename hasOlderMessages/loadOlderMessages to hasPreviousPage/
fetchPreviousPage following TanStack Query conventions, add
isFetchingPreviousPage
- Use separate AbortController for pagination to avoid aborting
in-progress streams
- Fix Express query param type safety (typeof check vs as cast)
- Add test for paginated request failure
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* docs: re-generate docs after change
* fix: use typeof check for requestId in sendMessage handler
Consistent with getConversation handler — avoids unsafe as string cast
for Express query params.
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* fix: scroll management races and observer rapid-fire loop
- useScrollManagement no longer fires on status-only changes, preventing
scroll-to-bottom yank when "loading-older" spinner appears
- sendMessage aborts in-flight pagination to prevent status stomping
- fetchPreviousPage guards setStatus to not overwrite concurrent streams
- IntersectionObserver skips initial callback after re-subscription to
prevent rapid-fire pagination loop
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* fix: pagination error handling, type safety, and performance
- Server-sent error events during pagination now set error status
(hadError flag prevents status flashing to idle)
- Replace unsafe MutableRefObject cast with plain object type in fetchPage
- Restore reverse-scan-with-break for query_result in processStreamEvent
- Add ResizeObserver to keep scroll height ref fresh for async content
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* fix: simplify error handling, remove hadError flag
- Set error status directly in onError callback instead of threading
hadError flag through return value
- Remove freeze/unfreeze scroll momentum plumbing (not effective enough)
- Clean up unused code
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* fix: abort pagination on conversation switch, arm observer via rAF
- loadHistory aborts in-flight pagination to prevent stale messages
from old conversation being prepended
- Replace initialFire boolean with requestAnimationFrame arming so
short conversations where sentinel is genuinely visible still
trigger loading automatically
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* fix: enforce minimum delay before prepending older messages
Add MIN_PREVIOUS_PAGE_LOAD_MS to ensure scroll inertia fully settles
before fetchPreviousPage prepends older messages to the chat.
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
* fix: prevent scroll jump when loading-older indicator disappears
The useScrollManagement hook's useLayoutEffect only depended on
messages.length, so prevScrollHeightRef was never updated when the
loading indicator appeared. Adding status to the dependency array
lets the effect capture the correct scrollHeight before messages
are prepended, producing an accurate delta.
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
---------
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>1 parent b9a6bd8 commit 5dc8759
10 files changed
Lines changed: 598 additions & 152 deletions
File tree
- docs/docs/api/appkit-ui/genie
- packages
- appkit-ui/src/react/genie
- appkit/src
- connectors/genie
- plugins/genie
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
| 22 | + | |
21 | 23 | | |
22 | 24 | | |
23 | 25 | | |
| |||
Lines changed: 154 additions & 29 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
16 | 20 | | |
17 | 21 | | |
18 | 22 | | |
| |||
26 | 30 | | |
27 | 31 | | |
28 | 32 | | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
37 | 136 | | |
38 | | - | |
39 | | - | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
40 | 146 | | |
41 | 147 | | |
42 | 148 | | |
43 | 149 | | |
44 | 150 | | |
45 | 151 | | |
46 | 152 | | |
| 153 | + | |
| 154 | + | |
47 | 155 | | |
48 | 156 | | |
49 | 157 | | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
60 | 170 | | |
61 | 171 | | |
62 | 172 | | |
63 | 173 | | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
64 | 185 | | |
65 | 186 | | |
66 | 187 | | |
| |||
69 | 190 | | |
70 | 191 | | |
71 | 192 | | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
78 | 200 | | |
79 | | - | |
80 | | - | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
81 | 206 | | |
82 | 207 | | |
83 | 208 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
16 | 24 | | |
17 | 25 | | |
18 | 26 | | |
| |||
32 | 40 | | |
33 | 41 | | |
34 | 42 | | |
35 | | - | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
36 | 49 | | |
37 | 50 | | |
38 | 51 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
40 | 41 | | |
41 | 42 | | |
42 | 43 | | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
43 | 50 | | |
44 | 51 | | |
45 | 52 | | |
| |||
0 commit comments