Skip to content

feat(appkit-ui): add Genie chat React components#116

Merged
calvarjorge merged 6 commits intofeat/genie-pluginfrom
feat/genie-dev-playground
Mar 2, 2026
Merged

feat(appkit-ui): add Genie chat React components#116
calvarjorge merged 6 commits intofeat/genie-pluginfrom
feat/genie-dev-playground

Conversation

@calvarjorge
Copy link
Contributor

@calvarjorge calvarjorge commented Feb 18, 2026

Summary

  • Add plug-and-play React components in @databricks/appkit-ui for Genie: GenieChat, GenieChatMessage, GenieChatMessageList, GenieChatInput, and useGenieChat hook
  • Hook manages SSE streaming, URL-based conversation persistence (?conversationId=xxx), and history replay on page refresh
  • Messages render with markdown (via marked)
  • Add Genie demo page to dev-playground at /genie with nav link and home page card

Add plug-and-play React components for Genie AI/BI chat:

- useGenieChat hook: manages SSE streaming, conversation persistence
  via URL params, and history replay on page refresh
- GenieChat: all-in-one component wiring hook + UI
- GenieChatMessage: renders messages with markdown (via marked),
  avatars, and collapsible SQL query attachments
- GenieChatMessageList: scrollable message list with auto-scroll,
  loading skeletons, and streaming status indicator
- GenieChatInput: textarea with Enter-to-send and auto-resize

Also adds Genie demo page to dev-playground at /genie and fixes
conversation history ordering in the backend (reverse to chronological).

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
@calvarjorge calvarjorge changed the base branch from main to feat/genie-plugin February 18, 2026 16:37
@calvarjorge calvarjorge force-pushed the feat/genie-dev-playground branch from 2266458 to 5907634 Compare February 24, 2026 11:10
Copy link
Member

@pkosiec pkosiec left a comment

Choose a reason for hiding this comment

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

Overall it looks really nice 👌 Well done! A few comments from my side.

OTEL_EXPORTER_OTLP_ENDPOINT='http://localhost:4318'
OTEL_RESOURCE_ATTRIBUTES='service.sample_attribute=dev'
OTEL_SERVICE_NAME='dev-playground'
GENIE_SPACE_ID=
Copy link
Member

Choose a reason for hiding this comment

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

It should be DATABRICKS_GENIE_SPACE_ID, right?
Image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, a bit more clear this way, changed. There's an important consideration here. For Genie, appkit does not automatically read the env vars to determine the Genie space. Because multiple Genie spaces are supported by the plugin, the space IDs are passed when creating the plugin as a parameter (dict of alias to space id). This is opposed to the Analytics plugin where we can just use the only env var to determine the warehouse.

This is why the code was working as is without the DATABRICKS_GENIE_SPACE_ID var set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Btw, I discussed this with Mario, and will implement a default behavior when the Genie plugin is created without config.

Copy link
Member

Choose a reason for hiding this comment

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

Could you please double check the styling? Here are some issues I found:
https://github.com/user-attachments/assets/d8f6b8c7-2a48-47d6-8bb2-c9672413ca18

Also:

Image

Copy link
Member

Choose a reason for hiding this comment

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

Also, we should ensure those components are documented in the same way as data / or general UI components.
I think we don't need a live preview but we do need at least example usage - see e.g.
https://databricks.github.io/appkit/docs/api/appkit-ui/data/AreaChart

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure, i documented them

Comment on lines +37 to +44
<Button
variant="ghost"
size="sm"
onClick={reset}
className="text-xs text-muted-foreground"
>
New conversation
</Button>
Copy link
Member

Choose a reason for hiding this comment

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

Just a suggestion - maybe we could put the button on the top right side near the "Genie chat" header? I think that would be a most common place for such button.

Image


return allMessages.slice(0, maxMessages);
// Genie API returns newest-first; reverse to chronological order
return allMessages.slice(0, maxMessages).reverse();
Copy link
Collaborator

@MarioCadenas MarioCadenas Feb 26, 2026

Choose a reason for hiding this comment

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

I wonder if we should fetch all at once or use the pagination to lazy load the messages an in the chat instead? this seems like it will have really bad performance (specially in the UI) if a chat has thousands of messages

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, for long conversations, this is something needed. I'm not sure how common this case is, but we should probably handle it. Do you mind if I implement this in a subsequent PR to avoid continuing to build on this one?

- Rename GENIE_SPACE_ID to DATABRICKS_GENIE_SPACE_ID in env and code
- Hide textarea scrollbar; only show overflow-y when content exceeds max height
- Skip rendering empty assistant bubbles during loading, show only the spinner
- Remove shadow from nested SQL query cards to fix corner shadow artifacts
- Move "New conversation" button to top-right of chat widget

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
Extend the doc generator to scan genie, multi-genie, and chat component
directories. Add JSDoc descriptions to all Genie components and props,
create usage examples for GenieChat and MultiGenieChat, and generate
8 new doc pages under a "Genie components" sidebar category.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
@calvarjorge calvarjorge force-pushed the feat/genie-dev-playground branch from 8ccb407 to 410fcae Compare February 26, 2026 13:19
Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
@calvarjorge calvarjorge force-pushed the feat/genie-dev-playground branch from 35ceb8f to 758557b Compare February 26, 2026 16:19
…ction

Generate a UUID per request in useGenieChat and pass it as ?requestId
to the sendMessage and loadHistory SSE endpoints. This allows the server
to use a stable streamId for reconnection and missed-event replay.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
@calvarjorge calvarjorge merged commit 9e3ab06 into feat/genie-plugin Mar 2, 2026
@calvarjorge calvarjorge deleted the feat/genie-dev-playground branch March 2, 2026 09:16
calvarjorge added a commit that referenced this pull request Mar 2, 2026
* feat(appkit): add Genie plugin for AI/BI space integration

Add a new Genie plugin that provides an opinionated chat API powered by
Databricks AI/BI Genie spaces. Users configure named space aliases in
plugin config, and the backend resolves aliases to actual space IDs.

Key design:
- Single SSE endpoint: POST /api/genie/:alias/messages
- Always executes as user (OBO) via asUser(req)
- SSE event flow: message_start → status (×N) → message_result → query_result (×N)
- Space alias abstraction keeps space IDs out of URLs and client code
- No cache/retry (chat is stateful and non-idempotent)
- Configurable timeout (default 2min, 0 for indefinite)

Also fixes pre-existing ajv type resolution issue in shared package
where pnpm hoisting caused TypeScript to resolve ajv@6 types instead
of the declared ajv@8 dependency.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* fix(shared): handle ajv v6/v8 ErrorObject type differences

Cast error entries to `any` when mapping validation errors so the code
works regardless of which ajv version TypeScript resolves (v6 has
`dataPath`, v8 has `instancePath`).

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* docs: regenerate API docs for genie plugin export

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* fix(appkit): copy genie manifest.json to dist during build

Add genie manifest.json to tsdown copy config so it's available at
runtime when loading from the built dist/ output.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* feat(appkit): add conversation history endpoint to Genie plugin

SSE endpoint (GET /api/genie/:alias/conversations/:conversationId)
that replays full conversation history reusing existing event types,
enabling page refresh without losing chat state.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* fix(appkit): simplify genie plugin imports per PR feedback

Use top-level @databricks/sdk-experimental export for Time/TimeUnits
and import createLogger from logging index instead of direct file path.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* docs(appkit): add @internal to genie export and document plugin in plugins.md

Add @internal JSDoc to genie const to exclude it from generated API
docs. Add Genie plugin section to plugins.md covering configuration,
endpoints, SSE events, and programmatic usage.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* docs: remove Variable.genie from generated API docs sidebar and index

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* feat(genie): add SSE reconnection support via stable streamId

Pass deterministic streamId values to StreamManager so clients can
reconnect and replay missed events using Last-Event-ID. Also adds a
default bufferSize of 100 to the Genie stream settings.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* feat(genie): default spaces config from DATABRICKS_GENIE_SPACE_ID env var

Make the `spaces` config optional. When omitted, fall back to
{ default: DATABRICKS_GENIE_SPACE_ID }. If the env var is also unset,
routes gracefully 404 instead of crashing at startup.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* refactor(genie): extract pollWaiter to simplify _handleSendMessage

Replace manual concurrency code (statusQueue, notifyGenerator,
waiterDone, waiterError, IIFE promise chain) with a reusable
pollWaiter async generator that bridges callback-based
waiter.wait({ onProgress }) into a for-await-of loop.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* feat(genie): use client-provided requestId query param as SSE streamId

Replace conversation-derived stream IDs with a client-provided
?requestId=<uuid> query param, enabling reliable SSE reconnection.
Falls back to crypto.randomUUID() when not provided.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* feat(appkit-ui): add Genie chat React components (#116)

* feat(appkit-ui): add Genie chat React components and dev-playground demo

Add plug-and-play React components for Genie AI/BI chat:

- useGenieChat hook: manages SSE streaming, conversation persistence
  via URL params, and history replay on page refresh
- GenieChat: all-in-one component wiring hook + UI
- GenieChatMessage: renders messages with markdown (via marked),
  avatars, and collapsible SQL query attachments
- GenieChatMessageList: scrollable message list with auto-scroll,
  loading skeletons, and streaming status indicator
- GenieChatInput: textarea with Enter-to-send and auto-resize

Also adds Genie demo page to dev-playground at /genie and fixes
conversation history ordering in the backend (reverse to chronological).

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* fix(appkit-ui): address Genie chat UI review feedback

- Rename GENIE_SPACE_ID to DATABRICKS_GENIE_SPACE_ID in env and code
- Hide textarea scrollbar; only show overflow-y when content exceeds max height
- Skip rendering empty assistant bubbles during loading, show only the spinner
- Remove shadow from nested SQL query cards to fix corner shadow artifacts
- Move "New conversation" button to top-right of chat widget

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* docs: add Genie component documentation with examples

Extend the doc generator to scan genie, multi-genie, and chat component
directories. Add JSDoc descriptions to all Genie components and props,
create usage examples for GenieChat and MultiGenieChat, and generate
8 new doc pages under a "Genie components" sidebar category.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* feat(genie): send requestId query param from frontend for SSE reconnection

Generate a UUID per request in useGenieChat and pass it as ?requestId
to the sendMessage and loadHistory SSE endpoints. This allows the server
to use a stable streamId for reconnection and missed-event replay.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

---------

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* Refactor Genie into Genie connector (#145)

* feat(appkit-ui): add Genie chat React components and dev-playground demo

Add plug-and-play React components for Genie AI/BI chat:

- useGenieChat hook: manages SSE streaming, conversation persistence
  via URL params, and history replay on page refresh
- GenieChat: all-in-one component wiring hook + UI
- GenieChatMessage: renders messages with markdown (via marked),
  avatars, and collapsible SQL query attachments
- GenieChatMessageList: scrollable message list with auto-scroll,
  loading skeletons, and streaming status indicator
- GenieChatInput: textarea with Enter-to-send and auto-resize

Also adds Genie demo page to dev-playground at /genie and fixes
conversation history ordering in the backend (reverse to chronological).

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* fix(appkit-ui): address Genie chat UI review feedback

- Rename GENIE_SPACE_ID to DATABRICKS_GENIE_SPACE_ID in env and code
- Hide textarea scrollbar; only show overflow-y when content exceeds max height
- Skip rendering empty assistant bubbles during loading, show only the spinner
- Remove shadow from nested SQL query cards to fix corner shadow artifacts
- Move "New conversation" button to top-right of chat widget

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* docs: add Genie component documentation with examples

Extend the doc generator to scan genie, multi-genie, and chat component
directories. Add JSDoc descriptions to all Genie components and props,
create usage examples for GenieChat and MultiGenieChat, and generate
8 new doc pages under a "Genie components" sidebar category.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* feat(genie): send requestId query param from frontend for SSE reconnection

Generate a UUID per request in useGenieChat and pass it as ?requestId
to the sendMessage and loadHistory SSE endpoints. This allows the server
to use a stable streamId for reconnection and missed-event replay.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* chore: genie connector

* refactor(genie): replace sendMessage with streaming implementation

The old non-streaming sendMessage (returning Promise<GenieMessageResponse>)
is replaced by the streaming version (returning AsyncGenerator<GenieStreamEvent>).

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

---------

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
Co-authored-by: MarioCadenas <MarioCadenas@users.noreply.github.com>

* chore: remove crypto/index.ts

* refactor: move shared Genie types to packages/shared

Move GenieStreamEvent, GenieMessageResponse, and GenieAttachmentResponse
to the shared package to eliminate duplication between appkit and
appkit-ui, ensuring both sides stay in sync.

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

* chore: colocate poll-waiter test with its source

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>

---------

Signed-off-by: Jorge Calvar <jorge.calvar@databricks.com>
Co-authored-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
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.

3 participants