Skip to content

feat: lazy loading for Genie conversations#163

Open
calvarjorge wants to merge 7 commits intomainfrom
jorge.calvar/genie_sequential_loading
Open

feat: lazy loading for Genie conversations#163
calvarjorge wants to merge 7 commits intomainfrom
jorge.calvar/genie_sequential_loading

Conversation

@calvarjorge
Copy link
Contributor

@calvarjorge calvarjorge commented Mar 5, 2026

Summary

  • When loading a Genie conversation, only the most recent page of messages is fetched initially (configurable via initialPageSize, default 20)
  • Older messages are loaded on demand when the user scrolls to the top
  • Adds a history_info SSE event that communicates pagination state (nextPageToken) to the frontend
  • Scroll position is preserved when older messages are prepended

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>
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>
- 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>
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>
Extract useScrollManagement and useLoadOlderOnScroll into custom hooks,
inline StreamingIndicator, leaving the component as clean JSX.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
- useLoadOlderOnScroll owns its sentinel ref internally
- Simplify message rendering with filter+map

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
- 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>
Comment on lines +426 to +427
hasOlderMessages,
loadOlderMessages,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we could rename hasOlderMessages to hasPreviousPage and loadOlderMessages to fetchPreviousPage? To use a naming known from e.g. tanstack query: https://tanstack.com/query/latest/docs/framework/react/reference/useInfiniteQuery?from=reactQueryV3

What. do you think?

BTW maybe isFetchingPreviousPage would be also helpful?

}

const includeQueryResults = req.query.includeQueryResults !== "false";
const pageToken = req.query.pageToken as string | undefined;
Copy link
Member

@pkosiec pkosiec Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Express query params can be [key: string]: undefined | string | ParsedQs | (string | ParsedQs)[];

convId: string,
options?: { pageToken?: string; errorMessage?: string },
) => {
abortControllerRef.current?.abort();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pagination should have separate abortControllerRef - if a user scrolls to the top during an active stream (status "streaming"), loadOlderMessages -> fetchPage will abort the in-progress streaming connection

});
});

describe("getConversation with pageToken", () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're missing a test case for pagination request failure - can you add it pls?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants