Skip to content

Commit c3581d5

Browse files
feat(appkit): add Genie plugin for AI/BI space integration (#108)
* 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>
1 parent c3f6524 commit c3581d5

45 files changed

Lines changed: 3487 additions & 72 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/dev-playground/.env.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ NODE_ENV='development'
66
OTEL_EXPORTER_OTLP_ENDPOINT='http://localhost:4318'
77
OTEL_RESOURCE_ATTRIBUTES='service.sample_attribute=dev'
88
OTEL_SERVICE_NAME='dev-playground'
9+
DATABRICKS_GENIE_SPACE_ID=
910
LAKEBASE_ENDPOINT='' # Run: databricks postgres list-endpoints projects/{project-id}/branches/{branch-id} — use the `name` field from the output
1011
PGHOST=
1112
PGUSER=

apps/dev-playground/client/src/routeTree.gen.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import { Route as TypeSafetyRouteRouteImport } from './routes/type-safety.route'
1313
import { Route as TelemetryRouteRouteImport } from './routes/telemetry.route'
1414
import { Route as SqlHelpersRouteRouteImport } from './routes/sql-helpers.route'
1515
import { Route as ReconnectRouteRouteImport } from './routes/reconnect.route'
16+
17+
import { Route as GenieRouteRouteImport } from './routes/genie.route'
18+
1619
import { Route as LakebaseRouteRouteImport } from './routes/lakebase.route'
20+
1721
import { Route as DataVisualizationRouteRouteImport } from './routes/data-visualization.route'
1822
import { Route as ArrowAnalyticsRouteRouteImport } from './routes/arrow-analytics.route'
1923
import { Route as AnalyticsRouteRouteImport } from './routes/analytics.route'
@@ -39,6 +43,12 @@ const ReconnectRouteRoute = ReconnectRouteRouteImport.update({
3943
path: '/reconnect',
4044
getParentRoute: () => rootRouteImport,
4145
} as any)
46+
47+
const GenieRouteRoute = GenieRouteRouteImport.update({
48+
id: '/genie',
49+
path: '/genie',
50+
getParentRoute: () => rootRouteImport,
51+
} as any)
4252
const LakebaseRouteRoute = LakebaseRouteRouteImport.update({
4353
id: '/lakebase',
4454
path: '/lakebase',
@@ -70,7 +80,11 @@ export interface FileRoutesByFullPath {
7080
'/analytics': typeof AnalyticsRouteRoute
7181
'/arrow-analytics': typeof ArrowAnalyticsRouteRoute
7282
'/data-visualization': typeof DataVisualizationRouteRoute
83+
84+
'/genie': typeof GenieRouteRoute
85+
7386
'/lakebase': typeof LakebaseRouteRoute
87+
7488
'/reconnect': typeof ReconnectRouteRoute
7589
'/sql-helpers': typeof SqlHelpersRouteRoute
7690
'/telemetry': typeof TelemetryRouteRoute
@@ -81,7 +95,11 @@ export interface FileRoutesByTo {
8195
'/analytics': typeof AnalyticsRouteRoute
8296
'/arrow-analytics': typeof ArrowAnalyticsRouteRoute
8397
'/data-visualization': typeof DataVisualizationRouteRoute
98+
99+
'/genie': typeof GenieRouteRoute
100+
84101
'/lakebase': typeof LakebaseRouteRoute
102+
85103
'/reconnect': typeof ReconnectRouteRoute
86104
'/sql-helpers': typeof SqlHelpersRouteRoute
87105
'/telemetry': typeof TelemetryRouteRoute
@@ -93,7 +111,11 @@ export interface FileRoutesById {
93111
'/analytics': typeof AnalyticsRouteRoute
94112
'/arrow-analytics': typeof ArrowAnalyticsRouteRoute
95113
'/data-visualization': typeof DataVisualizationRouteRoute
114+
115+
'/genie': typeof GenieRouteRoute
116+
96117
'/lakebase': typeof LakebaseRouteRoute
118+
97119
'/reconnect': typeof ReconnectRouteRoute
98120
'/sql-helpers': typeof SqlHelpersRouteRoute
99121
'/telemetry': typeof TelemetryRouteRoute
@@ -106,7 +128,11 @@ export interface FileRouteTypes {
106128
| '/analytics'
107129
| '/arrow-analytics'
108130
| '/data-visualization'
131+
132+
| '/genie'
133+
109134
| '/lakebase'
135+
110136
| '/reconnect'
111137
| '/sql-helpers'
112138
| '/telemetry'
@@ -117,7 +143,11 @@ export interface FileRouteTypes {
117143
| '/analytics'
118144
| '/arrow-analytics'
119145
| '/data-visualization'
146+
147+
| '/genie'
148+
120149
| '/lakebase'
150+
121151
| '/reconnect'
122152
| '/sql-helpers'
123153
| '/telemetry'
@@ -128,7 +158,11 @@ export interface FileRouteTypes {
128158
| '/analytics'
129159
| '/arrow-analytics'
130160
| '/data-visualization'
161+
162+
| '/genie'
163+
131164
| '/lakebase'
165+
132166
| '/reconnect'
133167
| '/sql-helpers'
134168
| '/telemetry'
@@ -140,7 +174,11 @@ export interface RootRouteChildren {
140174
AnalyticsRouteRoute: typeof AnalyticsRouteRoute
141175
ArrowAnalyticsRouteRoute: typeof ArrowAnalyticsRouteRoute
142176
DataVisualizationRouteRoute: typeof DataVisualizationRouteRoute
177+
178+
GenieRouteRoute: typeof GenieRouteRoute
179+
143180
LakebaseRouteRoute: typeof LakebaseRouteRoute
181+
144182
ReconnectRouteRoute: typeof ReconnectRouteRoute
145183
SqlHelpersRouteRoute: typeof SqlHelpersRouteRoute
146184
TelemetryRouteRoute: typeof TelemetryRouteRoute
@@ -177,6 +215,14 @@ declare module '@tanstack/react-router' {
177215
preLoaderRoute: typeof ReconnectRouteRouteImport
178216
parentRoute: typeof rootRouteImport
179217
}
218+
219+
'/genie': {
220+
id: '/genie'
221+
path: '/genie'
222+
fullPath: '/genie'
223+
preLoaderRoute: typeof GenieRouteRouteImport
224+
parentRoute: typeof rootRouteImport
225+
}
180226
'/lakebase': {
181227
id: '/lakebase'
182228
path: '/lakebase'
@@ -220,7 +266,11 @@ const rootRouteChildren: RootRouteChildren = {
220266
AnalyticsRouteRoute: AnalyticsRouteRoute,
221267
ArrowAnalyticsRouteRoute: ArrowAnalyticsRouteRoute,
222268
DataVisualizationRouteRoute: DataVisualizationRouteRoute,
269+
270+
GenieRouteRoute: GenieRouteRoute,
271+
223272
LakebaseRouteRoute: LakebaseRouteRoute,
273+
224274
ReconnectRouteRoute: ReconnectRouteRoute,
225275
SqlHelpersRouteRoute: SqlHelpersRouteRoute,
226276
TelemetryRouteRoute: TelemetryRouteRoute,

apps/dev-playground/client/src/routes/__root.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ function RootComponent() {
8080
SQL Helpers
8181
</Button>
8282
</Link>
83+
<Link to="/genie" className="no-underline">
84+
<Button
85+
variant="ghost"
86+
className="text-foreground hover:text-secondary-foreground"
87+
>
88+
Genie
89+
</Button>
90+
</Link>
8391
<ThemeSelector />
8492
</div>
8593
</nav>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { GenieChat } from "@databricks/appkit-ui/react";
2+
import { createFileRoute } from "@tanstack/react-router";
3+
4+
export const Route = createFileRoute("/genie")({
5+
component: GenieRoute,
6+
});
7+
8+
function GenieRoute() {
9+
return (
10+
<div className="min-h-screen bg-background">
11+
<main className="max-w-4xl mx-auto px-6 py-12">
12+
<div className="flex flex-col gap-6">
13+
<div>
14+
<h1 className="text-3xl font-bold tracking-tight text-foreground">
15+
Genie Chat
16+
</h1>
17+
<p className="text-muted-foreground mt-2">
18+
Ask natural language questions about your data using AI/BI Genie.
19+
</p>
20+
</div>
21+
22+
<div className="border rounded-lg h-[600px] flex flex-col">
23+
<GenieChat alias="demo" />
24+
</div>
25+
</div>
26+
</main>
27+
</div>
28+
);
29+
}

apps/dev-playground/client/src/routes/index.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,25 @@ function IndexRoute() {
163163
</div>
164164
</Card>
165165

166+
<Card className="p-6 hover:shadow-lg transition-shadow cursor-pointer">
167+
<div className="flex flex-col h-full">
168+
<h3 className="text-2xl font-semibold text-foreground mb-3">
169+
Genie Chat
170+
</h3>
171+
<p className="text-muted-foreground mb-6 flex-grow">
172+
Ask natural language questions about your data using AI/BI
173+
Genie. Features SSE streaming, markdown rendering, and
174+
conversation persistence.
175+
</p>
176+
<Button
177+
onClick={() => navigate({ to: "/genie" })}
178+
className="w-full"
179+
>
180+
Try Genie Chat
181+
</Button>
182+
</div>
183+
</Card>
184+
166185
<Card className="p-6 hover:shadow-lg transition-shadow cursor-pointer">
167186
<div className="flex flex-col h-full">
168187
<h3 className="text-2xl font-semibold text-foreground mb-3">

apps/dev-playground/server/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import "reflect-metadata";
2-
import { analytics, createApp, server } from "@databricks/appkit";
2+
import { analytics, createApp, genie, server } from "@databricks/appkit";
33
import { WorkspaceClient } from "@databricks/sdk-experimental";
44
import { lakebaseExamples } from "./lakebase-examples-plugin";
55
import { reconnect } from "./reconnect-plugin";
@@ -21,6 +21,9 @@ createApp({
2121
reconnect(),
2222
telemetryExamples(),
2323
analytics({}),
24+
genie({
25+
spaces: { demo: process.env.DATABRICKS_GENIE_SPACE_ID ?? "placeholder" },
26+
}),
2427
lakebaseExamples(),
2528
],
2629
...(process.env.APPKIT_E2E_TEST && { client: createMockClient() }),
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# GenieChat
2+
3+
Full-featured chat interface for a single Databricks AI/BI Genie space. Handles message streaming, conversation history, and auto-reconnection via SSE.
4+
5+
6+
## Example
7+
8+
```tsx
9+
import { GenieChat } from "@databricks/appkit-ui/react";
10+
11+
export default function GenieChatExample() {
12+
return (
13+
<div style={{ height: 500, border: "1px solid #e2e8f0", borderRadius: 8 }}>
14+
<GenieChat alias="my-space" />
15+
</div>
16+
);
17+
}
18+
19+
```
20+
21+
22+
## GenieChat
23+
24+
Full-featured chat interface for a single Databricks AI/BI Genie space. Handles message streaming, conversation history, and auto-reconnection via SSE.
25+
26+
27+
**Source:** [`packages/appkit-ui/src/react/genie/genie-chat.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/genie/genie-chat.tsx)
28+
29+
30+
### Props
31+
32+
| Prop | Type | Required | Default | Description |
33+
|------|------|----------|---------|-------------|
34+
| `alias` | `string` || - | Genie space alias (must match a key registered with the genie plugin on the server) |
35+
| `basePath` | `string` | | - | Base API path |
36+
| `placeholder` | `string` | | - | Placeholder text for the input |
37+
| `className` | `string` | | - | Additional CSS class for the root container |
38+
39+
40+
41+
### Usage
42+
43+
```tsx
44+
import { GenieChat } from '@databricks/appkit-ui';
45+
46+
<GenieChat /* props */ />
47+
```
48+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# GenieChatInput
2+
3+
Auto-expanding textarea input with a send button for chat messages. Submits on Enter (Shift+Enter for newline).
4+
5+
6+
## GenieChatInput
7+
8+
Auto-expanding textarea input with a send button for chat messages. Submits on Enter (Shift+Enter for newline).
9+
10+
11+
**Source:** [`packages/appkit-ui/src/react/genie/genie-chat-input.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/genie/genie-chat-input.tsx)
12+
13+
14+
### Props
15+
16+
| Prop | Type | Required | Default | Description |
17+
|------|------|----------|---------|-------------|
18+
| `onSend` | `(content: string) => void` || - | Callback fired when the user submits a message |
19+
| `disabled` | `boolean` | | `false` | Disable the input and send button |
20+
| `placeholder` | `string` | | `Ask a question...` | Placeholder text shown in the textarea |
21+
| `className` | `string` | | - | Additional CSS class for the container |
22+
23+
24+
25+
### Usage
26+
27+
```tsx
28+
import { GenieChatInput } from '@databricks/appkit-ui';
29+
30+
<GenieChatInput /* props */ />
31+
```
32+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# GenieChatMessage
2+
3+
Renders a single Genie message bubble with optional expandable SQL query attachments.
4+
5+
6+
## GenieChatMessage
7+
8+
Renders a single Genie message bubble with optional expandable SQL query attachments.
9+
10+
11+
**Source:** [`packages/appkit-ui/src/react/genie/genie-chat-message.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/genie/genie-chat-message.tsx)
12+
13+
14+
### Props
15+
16+
| Prop | Type | Required | Default | Description |
17+
|------|------|----------|---------|-------------|
18+
| `message` | `GenieMessageItem` || - | The message object to render |
19+
| `className` | `string` | | - | Additional CSS class |
20+
21+
22+
23+
### Usage
24+
25+
```tsx
26+
import { GenieChatMessage } from '@databricks/appkit-ui';
27+
28+
<GenieChatMessage /* props */ />
29+
```
30+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# GenieChatMessageList
2+
3+
Scrollable message list that renders Genie chat messages with auto-scroll, skeleton loaders, and a streaming indicator.
4+
5+
6+
## GenieChatMessageList
7+
8+
Scrollable message list that renders Genie chat messages with auto-scroll, skeleton loaders, and a streaming indicator.
9+
10+
11+
**Source:** [`packages/appkit-ui/src/react/genie/genie-chat-message-list.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/genie/genie-chat-message-list.tsx)
12+
13+
14+
### Props
15+
16+
| Prop | Type | Required | Default | Description |
17+
|------|------|----------|---------|-------------|
18+
| `messages` | `GenieMessageItem[]` || - | Array of messages to display |
19+
| `status` | `enum` || - | Current chat status (controls loading indicators and skeleton placeholders) |
20+
| `className` | `string` | | - | Additional CSS class for the scroll area |
21+
22+
23+
24+
### Usage
25+
26+
```tsx
27+
import { GenieChatMessageList } from '@databricks/appkit-ui';
28+
29+
<GenieChatMessageList /* props */ />
30+
```
31+

0 commit comments

Comments
 (0)