Skip to content

Commit 3635710

Browse files
committed
feat(appkit): ui for the no-config chat
Signed-off-by: Hubert Zub <hubert.zub@databricks.com>
1 parent e2ded5a commit 3635710

26 files changed

Lines changed: 2202 additions & 0 deletions

apps/dev-playground/client/src/lib/nav.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ export const NAV_GROUPS: ReadonlyArray<NavGroup> = [
9898
"Chat agent over Databricks Model Serving with tools auto-discovered from AppKit plugins.",
9999
icon: BotIcon,
100100
},
101+
{
102+
to: "/agent-chat-full",
103+
label: "Custom Agent (full no-config chat)",
104+
description:
105+
"Drop-in <ChatApp /> with zero props — defaults to /api/agents/chat with the full UX.",
106+
icon: MessageCircleIcon,
107+
},
101108
{
102109
to: "/genie",
103110
label: "Genie",

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { Route as DataVisualizationRouteRouteImport } from './routes/data-visual
2525
import { Route as ChartInferenceRouteRouteImport } from './routes/chart-inference.route'
2626
import { Route as ArrowAnalyticsRouteRouteImport } from './routes/arrow-analytics.route'
2727
import { Route as AnalyticsRouteRouteImport } from './routes/analytics.route'
28+
import { Route as AgentChatFullRouteRouteImport } from './routes/agent-chat-full.route'
2829
import { Route as AgentRouteRouteImport } from './routes/agent.route'
2930
import { Route as IndexRouteImport } from './routes/index'
3031

@@ -108,6 +109,11 @@ const AnalyticsRouteRoute = AnalyticsRouteRouteImport.update({
108109
path: '/analytics',
109110
getParentRoute: () => rootRouteImport,
110111
} as any)
112+
const AgentChatFullRouteRoute = AgentChatFullRouteRouteImport.update({
113+
id: '/agent-chat-full',
114+
path: '/agent-chat-full',
115+
getParentRoute: () => rootRouteImport,
116+
} as any)
111117
const AgentRouteRoute = AgentRouteRouteImport.update({
112118
id: '/agent',
113119
path: '/agent',
@@ -122,6 +128,7 @@ const IndexRoute = IndexRouteImport.update({
122128
export interface FileRoutesByFullPath {
123129
'/': typeof IndexRoute
124130
'/agent': typeof AgentRouteRoute
131+
'/agent-chat-full': typeof AgentChatFullRouteRoute
125132
'/analytics': typeof AnalyticsRouteRoute
126133
'/arrow-analytics': typeof ArrowAnalyticsRouteRoute
127134
'/chart-inference': typeof ChartInferenceRouteRoute
@@ -142,6 +149,7 @@ export interface FileRoutesByFullPath {
142149
export interface FileRoutesByTo {
143150
'/': typeof IndexRoute
144151
'/agent': typeof AgentRouteRoute
152+
'/agent-chat-full': typeof AgentChatFullRouteRoute
145153
'/analytics': typeof AnalyticsRouteRoute
146154
'/arrow-analytics': typeof ArrowAnalyticsRouteRoute
147155
'/chart-inference': typeof ChartInferenceRouteRoute
@@ -163,6 +171,7 @@ export interface FileRoutesById {
163171
__root__: typeof rootRouteImport
164172
'/': typeof IndexRoute
165173
'/agent': typeof AgentRouteRoute
174+
'/agent-chat-full': typeof AgentChatFullRouteRoute
166175
'/analytics': typeof AnalyticsRouteRoute
167176
'/arrow-analytics': typeof ArrowAnalyticsRouteRoute
168177
'/chart-inference': typeof ChartInferenceRouteRoute
@@ -185,6 +194,7 @@ export interface FileRouteTypes {
185194
fullPaths:
186195
| '/'
187196
| '/agent'
197+
| '/agent-chat-full'
188198
| '/analytics'
189199
| '/arrow-analytics'
190200
| '/chart-inference'
@@ -205,6 +215,7 @@ export interface FileRouteTypes {
205215
to:
206216
| '/'
207217
| '/agent'
218+
| '/agent-chat-full'
208219
| '/analytics'
209220
| '/arrow-analytics'
210221
| '/chart-inference'
@@ -225,6 +236,7 @@ export interface FileRouteTypes {
225236
| '__root__'
226237
| '/'
227238
| '/agent'
239+
| '/agent-chat-full'
228240
| '/analytics'
229241
| '/arrow-analytics'
230242
| '/chart-inference'
@@ -246,6 +258,7 @@ export interface FileRouteTypes {
246258
export interface RootRouteChildren {
247259
IndexRoute: typeof IndexRoute
248260
AgentRouteRoute: typeof AgentRouteRoute
261+
AgentChatFullRouteRoute: typeof AgentChatFullRouteRoute
249262
AnalyticsRouteRoute: typeof AnalyticsRouteRoute
250263
ArrowAnalyticsRouteRoute: typeof ArrowAnalyticsRouteRoute
251264
ChartInferenceRouteRoute: typeof ChartInferenceRouteRoute
@@ -378,6 +391,13 @@ declare module '@tanstack/react-router' {
378391
preLoaderRoute: typeof AnalyticsRouteRouteImport
379392
parentRoute: typeof rootRouteImport
380393
}
394+
'/agent-chat-full': {
395+
id: '/agent-chat-full'
396+
path: '/agent-chat-full'
397+
fullPath: '/agent-chat-full'
398+
preLoaderRoute: typeof AgentChatFullRouteRouteImport
399+
parentRoute: typeof rootRouteImport
400+
}
381401
'/agent': {
382402
id: '/agent'
383403
path: '/agent'
@@ -398,6 +418,7 @@ declare module '@tanstack/react-router' {
398418
const rootRouteChildren: RootRouteChildren = {
399419
IndexRoute: IndexRoute,
400420
AgentRouteRoute: AgentRouteRoute,
421+
AgentChatFullRouteRoute: AgentChatFullRouteRoute,
401422
AnalyticsRouteRoute: AnalyticsRouteRoute,
402423
ArrowAnalyticsRouteRoute: ArrowAnalyticsRouteRoute,
403424
ChartInferenceRouteRoute: ChartInferenceRouteRoute,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ChatApp } from "@databricks/appkit-ui/react/chat";
2+
import { createFileRoute } from "@tanstack/react-router";
3+
4+
export const Route = createFileRoute("/agent-chat-full")({
5+
component: AgentChatFullRoute,
6+
});
7+
8+
/**
9+
* No-config showcase of `<ChatApp />`. For the build-your-own variant
10+
* that wires `useChat` by hand, see `agent.route.tsx`.
11+
*/
12+
function AgentChatFullRoute() {
13+
return (
14+
<div className="h-svh bg-background">
15+
<ChatApp api="/api/agents/chat" />
16+
</div>
17+
);
18+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# ApprovalEntry
2+
3+
Approval-gate lifecycle, owned by `ChatApp`.
4+
5+
6+
## ApprovalEntry
7+
8+
Approval-gate lifecycle, owned by `ChatApp`.
9+
10+
11+
**Source:** [`packages/appkit-ui/src/react/chat/app/chat-app.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/chat/app/chat-app.tsx)
12+
13+
14+
### Props
15+
16+
This component extends standard HTML element attributes.
17+
18+
19+
20+
### Usage
21+
22+
```tsx
23+
import { ApprovalEntry } from '@databricks/appkit-ui';
24+
25+
<ApprovalEntry /* props */ />
26+
```
27+
28+
29+
## ChatApp
30+
31+
Drop-in chat against an `agents()`-backed AppKit server. Fills its
32+
parent — provide a sized container. For history, feedback, or file
33+
attachments, wrap `useChat` directly instead.
34+
35+
36+
**Source:** [`packages/appkit-ui/src/react/chat/app/chat-app.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/chat/app/chat-app.tsx)
37+
38+
39+
### Props
40+
41+
| Prop | Type | Required | Default | Description |
42+
|------|------|----------|---------|-------------|
43+
| `api` | `string` | | `/api/agents/chat` | Chat endpoint URL. Defaults to `/api/agents/chat`. |
44+
| `emptyState` | `ReactNode` | | - | Empty-state node shown when there are no messages yet. |
45+
| `placeholder` | `string` | | - | Placeholder text for the default composer. |
46+
| `composerCaption` | `string \| null` | | - | Caption shown below the composer. Pass `null` to suppress. |
47+
| `renderMessage` | `((props: ChatMessageProps<TMessage>) => ReactNode)` | | - | Override per-message rendering. Return undefined to fall through. |
48+
| `renderToolCall` | `((props: ChatToolCallProps) => ReactNode)` | | - | Override tool-call rendering. Return undefined to fall through. |
49+
| `renderComposer` | `((props: ChatComposerProps<TMessage>) => ReactNode)` | | - | Override the composer entirely. |
50+
| `onApprovalDecision` | `((decision: ApprovalDecision) => void \| Promise<void>)` | | - | Called when the user clicks Allow / Deny. Throw to keep the card `pending`. Defaults to a POST against approveUrl. |
51+
| `approveUrl` | `string` | | - | Defaults to `api` with `/chat` swapped for `/approve`. |
52+
| `className` | `string` | | - | - |
53+
| `id` | `string` | | - | Stable chat id. Defaults to a fresh UUID per mount. |
54+
| `onError` | `((error: Error) => void)` | | - | Called on stream errors. |
55+
| `headers` | `Resolvable<Record<string, string> \| Headers>` | | - | Extra fetch headers forwarded to the transport. |
56+
| `messages` | `TMessage[]` | | - | Initial messages (e.g. when hydrating from history). |
57+
| `onData` | `ChatOnDataCallback<TMessage>` | | - | Fires for every `data-*` chunk (e.g. `data-approval-pending`). |
58+
| `onStreamPart` | `((chunk: UIMessageChunk) => void)` | | - | Fires synchronously for every chunk. Unaffected by render throttling. |
59+
60+
61+
62+
### Usage
63+
64+
```tsx
65+
import { ChatApp } from '@databricks/appkit-ui';
66+
67+
<ChatApp /* props */ />
68+
```
69+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# ChatAwaitingResponse
2+
3+
"Generating response" placeholder shown before the first chunk arrives.
4+
5+
6+
## ChatAwaitingResponse
7+
8+
"Generating response" placeholder shown before the first chunk arrives.
9+
10+
11+
**Source:** [`packages/appkit-ui/src/react/chat/app/chat-awaiting-response.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/chat/app/chat-awaiting-response.tsx)
12+
13+
14+
### Props
15+
16+
| Prop | Type | Required | Default | Description |
17+
|------|------|----------|---------|-------------|
18+
| `label` | `string` | | `Generating response` | Customise the shimmering label. |
19+
| `className` | `string` | | - | - |
20+
21+
22+
23+
### Usage
24+
25+
```tsx
26+
import { ChatAwaitingResponse } from '@databricks/appkit-ui';
27+
28+
<ChatAwaitingResponse /* props */ />
29+
```
30+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# ChatComposer
2+
3+
Default composer. Built on the `&lt;ChatInput&gt;` headless primitive so Enter-to-submit, IME composition, and value clearing match roll-your-own composers.
4+
5+
6+
## ChatComposer
7+
8+
Default composer. Built on the `&lt;ChatInput&gt;` headless primitive so
9+
Enter-to-submit, IME composition, and value clearing match
10+
roll-your-own composers.
11+
12+
13+
**Source:** [`packages/appkit-ui/src/react/chat/app/chat-composer.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/chat/app/chat-composer.tsx)
14+
15+
16+
### Props
17+
18+
| Prop | Type | Required | Default | Description |
19+
|------|------|----------|---------|-------------|
20+
| `sendMessage` | `(message?: (Omit<TMessage, "id" \| "role"> & { id?: TMessage["id"] \| undefined; role?: TMessage["role"] \| undefined; } & { text?: undefined; files?: undefined; messageId?: string \| undefined; }) \| { ...; } \| { ...; } \| undefined, options?: ChatRequestOptions \| undefined) => Promise<...>` || - | - |
21+
| `status` | `enum` || - | - |
22+
| `stop` | `() => void` || - | - |
23+
| `placeholder` | `string` | | `Ask a question…` | - |
24+
| `caption` | `string \| null` | | `Always review the accuracy of responses.` | Caption below the input. Pass `null` to suppress. |
25+
| `className` | `string` | | - | - |
26+
27+
28+
29+
### Usage
30+
31+
```tsx
32+
import { ChatComposer } from '@databricks/appkit-ui';
33+
34+
<ChatComposer /* props */ />
35+
```
36+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# ChatGreeting
2+
3+
Empty-state hero shown above the composer before the first message.
4+
5+
6+
## ChatGreeting
7+
8+
Empty-state hero shown above the composer before the first message.
9+
10+
11+
**Source:** [`packages/appkit-ui/src/react/chat/app/chat-greeting.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/chat/app/chat-greeting.tsx)
12+
13+
14+
### Props
15+
16+
| Prop | Type | Required | Default | Description |
17+
|------|------|----------|---------|-------------|
18+
| `title` | `ReactNode` | | `What would you like to know?` | Headline shown when the conversation is empty. |
19+
| `subtitle` | `ReactNode` | | - | Optional subtitle below the headline. |
20+
| `className` | `string` | | - | - |
21+
22+
23+
24+
### Usage
25+
26+
```tsx
27+
import { ChatGreeting } from '@databricks/appkit-ui';
28+
29+
<ChatGreeting /* props */ />
30+
```
31+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# ChatMessage
2+
3+
Renders a single chat message. Unknown part types are ignored so older builds don't break on future protocol additions.
4+
5+
6+
## ChatMessage
7+
8+
Renders a single chat message. Unknown part types are ignored so
9+
older builds don't break on future protocol additions.
10+
11+
12+
**Source:** [`packages/appkit-ui/src/react/chat/app/chat-message.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/chat/app/chat-message.tsx)
13+
14+
15+
### Props
16+
17+
| Prop | Type | Required | Default | Description |
18+
|------|------|----------|---------|-------------|
19+
| `message` | `UIMessage<unknown, UIDataTypes, UITools>` || - | - |
20+
| `isLoading` | `boolean` || - | True while this message is the last assistant message and the chat is streaming. |
21+
| `approvals` | `Map<string, ApprovalEntry>` || - | Approval state map keyed by `approvalId`. |
22+
| `onApprove` | `(approvalId: string, streamId: string) => void` || - | - |
23+
| `onDeny` | `(approvalId: string, streamId: string) => void` || - | - |
24+
| `renderToolCall` | `((props: ChatToolCallProps) => ReactNode)` | | - | Return a ReactNode to override, `undefined` to fall through. |
25+
26+
27+
28+
### Usage
29+
30+
```tsx
31+
import { ChatMessage } from '@databricks/appkit-ui';
32+
33+
<ChatMessage /* props */ />
34+
```
35+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# ChatMessages
2+
3+
Auto-sticking conversation list with a floating scroll-to-bottom button when the user has scrolled up.
4+
5+
6+
## ChatMessages
7+
8+
Auto-sticking conversation list with a floating scroll-to-bottom
9+
button when the user has scrolled up.
10+
11+
12+
**Source:** [`packages/appkit-ui/src/react/chat/app/chat-messages.tsx`](https://github.com/databricks/appkit/blob/main/packages/appkit-ui/src/react/chat/app/chat-messages.tsx)
13+
14+
15+
### Props
16+
17+
| Prop | Type | Required | Default | Description |
18+
|------|------|----------|---------|-------------|
19+
| `messages` | `TMessage[]` || - | - |
20+
| `status` | `enum` || - | - |
21+
| `containerRef` | `RefObject<HTMLDivElement \| null>` || - | - |
22+
| `isAtBottom` | `boolean` || - | - |
23+
| `scrollToBottom` | `() => void` || - | - |
24+
| `approvals` | `Map<string, ApprovalEntry>` || - | - |
25+
| `onApprove` | `(approvalId: string, streamId: string) => void` || - | - |
26+
| `onDeny` | `(approvalId: string, streamId: string) => void` || - | - |
27+
| `renderMessage` | `((props: ChatMessageProps<TMessage>) => ReactNode)` | | - | Override per-message rendering. Return undefined to fall through. |
28+
| `renderToolCall` | `((props: ChatToolCallProps) => ReactNode)` | | - | Forwarded to each `&lt;ChatMessage&gt;`. |
29+
| `className` | `string` | | - | - |
30+
31+
32+
33+
### Usage
34+
35+
```tsx
36+
import { ChatMessages } from '@databricks/appkit-ui';
37+
38+
<ChatMessages /* props */ />
39+
```
40+

0 commit comments

Comments
 (0)