diff --git a/apps/dev-playground/.env.dist b/apps/dev-playground/.env.dist
index 23c3265a..80eda94b 100644
--- a/apps/dev-playground/.env.dist
+++ b/apps/dev-playground/.env.dist
@@ -9,6 +9,7 @@ OTEL_SERVICE_NAME='dev-playground'
DATABRICKS_VOLUME_PLAYGROUND=
DATABRICKS_VOLUME_OTHER=
DATABRICKS_GENIE_SPACE_ID=
+DATABRICKS_SERVING_ENDPOINT=
LAKEBASE_ENDPOINT='' # Run: databricks postgres list-endpoints projects/{project-id}/branches/{branch-id} — use the `name` field from the output
PGHOST=
PGUSER=
diff --git a/apps/dev-playground/client/.gitignore b/apps/dev-playground/client/.gitignore
index a547bf36..267b28f3 100644
--- a/apps/dev-playground/client/.gitignore
+++ b/apps/dev-playground/client/.gitignore
@@ -12,6 +12,9 @@ dist
dist-ssr
*.local
+# Auto-generated types (endpoint-specific, varies per developer)
+src/appKitServingTypes.d.ts
+
# Editor directories and files
.vscode/*
!.vscode/extensions.json
diff --git a/apps/dev-playground/client/src/routeTree.gen.ts b/apps/dev-playground/client/src/routeTree.gen.ts
index c4c38d14..99ac75fc 100644
--- a/apps/dev-playground/client/src/routeTree.gen.ts
+++ b/apps/dev-playground/client/src/routeTree.gen.ts
@@ -12,6 +12,7 @@ import { Route as rootRouteImport } from './routes/__root'
import { Route as TypeSafetyRouteRouteImport } from './routes/type-safety.route'
import { Route as TelemetryRouteRouteImport } from './routes/telemetry.route'
import { Route as SqlHelpersRouteRouteImport } from './routes/sql-helpers.route'
+import { Route as ServingRouteRouteImport } from './routes/serving.route'
import { Route as ReconnectRouteRouteImport } from './routes/reconnect.route'
import { Route as LakebaseRouteRouteImport } from './routes/lakebase.route'
import { Route as GenieRouteRouteImport } from './routes/genie.route'
@@ -37,6 +38,11 @@ const SqlHelpersRouteRoute = SqlHelpersRouteRouteImport.update({
path: '/sql-helpers',
getParentRoute: () => rootRouteImport,
} as any)
+const ServingRouteRoute = ServingRouteRouteImport.update({
+ id: '/serving',
+ path: '/serving',
+ getParentRoute: () => rootRouteImport,
+} as any)
const ReconnectRouteRoute = ReconnectRouteRouteImport.update({
id: '/reconnect',
path: '/reconnect',
@@ -93,6 +99,7 @@ export interface FileRoutesByFullPath {
'/genie': typeof GenieRouteRoute
'/lakebase': typeof LakebaseRouteRoute
'/reconnect': typeof ReconnectRouteRoute
+ '/serving': typeof ServingRouteRoute
'/sql-helpers': typeof SqlHelpersRouteRoute
'/telemetry': typeof TelemetryRouteRoute
'/type-safety': typeof TypeSafetyRouteRoute
@@ -107,6 +114,7 @@ export interface FileRoutesByTo {
'/genie': typeof GenieRouteRoute
'/lakebase': typeof LakebaseRouteRoute
'/reconnect': typeof ReconnectRouteRoute
+ '/serving': typeof ServingRouteRoute
'/sql-helpers': typeof SqlHelpersRouteRoute
'/telemetry': typeof TelemetryRouteRoute
'/type-safety': typeof TypeSafetyRouteRoute
@@ -122,6 +130,7 @@ export interface FileRoutesById {
'/genie': typeof GenieRouteRoute
'/lakebase': typeof LakebaseRouteRoute
'/reconnect': typeof ReconnectRouteRoute
+ '/serving': typeof ServingRouteRoute
'/sql-helpers': typeof SqlHelpersRouteRoute
'/telemetry': typeof TelemetryRouteRoute
'/type-safety': typeof TypeSafetyRouteRoute
@@ -138,6 +147,7 @@ export interface FileRouteTypes {
| '/genie'
| '/lakebase'
| '/reconnect'
+ | '/serving'
| '/sql-helpers'
| '/telemetry'
| '/type-safety'
@@ -152,6 +162,7 @@ export interface FileRouteTypes {
| '/genie'
| '/lakebase'
| '/reconnect'
+ | '/serving'
| '/sql-helpers'
| '/telemetry'
| '/type-safety'
@@ -166,6 +177,7 @@ export interface FileRouteTypes {
| '/genie'
| '/lakebase'
| '/reconnect'
+ | '/serving'
| '/sql-helpers'
| '/telemetry'
| '/type-safety'
@@ -181,6 +193,7 @@ export interface RootRouteChildren {
GenieRouteRoute: typeof GenieRouteRoute
LakebaseRouteRoute: typeof LakebaseRouteRoute
ReconnectRouteRoute: typeof ReconnectRouteRoute
+ ServingRouteRoute: typeof ServingRouteRoute
SqlHelpersRouteRoute: typeof SqlHelpersRouteRoute
TelemetryRouteRoute: typeof TelemetryRouteRoute
TypeSafetyRouteRoute: typeof TypeSafetyRouteRoute
@@ -209,6 +222,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof SqlHelpersRouteRouteImport
parentRoute: typeof rootRouteImport
}
+ '/serving': {
+ id: '/serving'
+ path: '/serving'
+ fullPath: '/serving'
+ preLoaderRoute: typeof ServingRouteRouteImport
+ parentRoute: typeof rootRouteImport
+ }
'/reconnect': {
id: '/reconnect'
path: '/reconnect'
@@ -285,6 +305,7 @@ const rootRouteChildren: RootRouteChildren = {
GenieRouteRoute: GenieRouteRoute,
LakebaseRouteRoute: LakebaseRouteRoute,
ReconnectRouteRoute: ReconnectRouteRoute,
+ ServingRouteRoute: ServingRouteRoute,
SqlHelpersRouteRoute: SqlHelpersRouteRoute,
TelemetryRouteRoute: TelemetryRouteRoute,
TypeSafetyRouteRoute: TypeSafetyRouteRoute,
diff --git a/apps/dev-playground/client/src/routes/__root.tsx b/apps/dev-playground/client/src/routes/__root.tsx
index 5cf74ce3..35a2282b 100644
--- a/apps/dev-playground/client/src/routes/__root.tsx
+++ b/apps/dev-playground/client/src/routes/__root.tsx
@@ -104,6 +104,14 @@ function RootComponent() {
Files
+
+
+
diff --git a/apps/dev-playground/client/src/routes/index.tsx b/apps/dev-playground/client/src/routes/index.tsx
index e331d93c..934b1467 100644
--- a/apps/dev-playground/client/src/routes/index.tsx
+++ b/apps/dev-playground/client/src/routes/index.tsx
@@ -218,6 +218,24 @@ function IndexRoute() {
+
+
+
+
+ Model Serving
+
+
+ Chat with a Databricks Model Serving endpoint using streaming
+ completions with real-time SSE responses.
+
+
+
+
diff --git a/apps/dev-playground/client/src/routes/serving.route.tsx b/apps/dev-playground/client/src/routes/serving.route.tsx
new file mode 100644
index 00000000..770d42f4
--- /dev/null
+++ b/apps/dev-playground/client/src/routes/serving.route.tsx
@@ -0,0 +1,148 @@
+import { useServingStream } from "@databricks/appkit-ui/react";
+import { createFileRoute } from "@tanstack/react-router";
+import { useEffect, useRef, useState } from "react";
+
+export const Route = createFileRoute("/serving")({
+ component: ServingRoute,
+});
+
+interface Message {
+ id: string;
+ role: "user" | "assistant";
+ content: string;
+}
+
+function extractContent(chunk: unknown): string {
+ return (
+ (chunk as { choices?: { delta?: { content?: string } }[] })?.choices?.[0]
+ ?.delta?.content ?? ""
+ );
+}
+
+function ServingRoute() {
+ const [input, setInput] = useState("");
+ const [messages, setMessages] = useState
([]);
+
+ const { stream, chunks, streaming, error, reset } = useServingStream({
+ messages: [],
+ });
+
+ const streamingContent = chunks.map(extractContent).join("");
+
+ // Commit assistant message when streaming transitions from true → false
+ const prevStreamingRef = useRef(false);
+ useEffect(() => {
+ if (prevStreamingRef.current && !streaming && streamingContent) {
+ setMessages((prev) => [
+ ...prev,
+ {
+ id: crypto.randomUUID(),
+ role: "assistant",
+ content: streamingContent,
+ },
+ ]);
+ reset();
+ }
+ prevStreamingRef.current = streaming;
+ }, [streaming, streamingContent, reset]);
+
+ function handleSubmit(e: React.FormEvent) {
+ e.preventDefault();
+ if (!input.trim() || streaming) return;
+
+ const userMessage: Message = {
+ id: crypto.randomUUID(),
+ role: "user",
+ content: input.trim(),
+ };
+
+ const fullMessages = [
+ ...messages.map(({ role, content }) => ({ role, content })),
+ { role: "user" as const, content: userMessage.content },
+ ];
+
+ setMessages((prev) => [...prev, userMessage]);
+ setInput("");
+ reset();
+ stream({ messages: fullMessages });
+ }
+
+ return (
+
+
+
+
+
+ Model Serving
+
+
+ Chat with a Databricks Model Serving endpoint. Set{" "}
+
+ DATABRICKS_SERVING_ENDPOINT
+ {" "}
+ to enable.
+
+
+
+
+ {/* Messages area */}
+
+ {messages.map((msg) => (
+
+ ))}
+
+ {/* Streaming response */}
+ {streaming && (
+
+
+
+ {streamingContent || "..."}
+
+
+
+ )}
+
+ {error && (
+
+ Error: {error}
+
+ )}
+
+
+ {/* Input area */}
+
+
+
+
+
+ );
+}
diff --git a/apps/dev-playground/client/vite.config.ts b/apps/dev-playground/client/vite.config.ts
index f892c62f..5f37880b 100644
--- a/apps/dev-playground/client/vite.config.ts
+++ b/apps/dev-playground/client/vite.config.ts
@@ -1,4 +1,5 @@
import path from "node:path";
+import { appKitServingTypesPlugin } from "@databricks/appkit";
import { tanstackRouter } from "@tanstack/router-plugin/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
@@ -11,6 +12,7 @@ export default defineConfig({
target: "react",
autoCodeSplitting: process.env.NODE_ENV !== "development",
}),
+ appKitServingTypesPlugin(),
],
server: {
hmr: {
diff --git a/apps/dev-playground/server/index.ts b/apps/dev-playground/server/index.ts
index a4b6a2c6..af05b11f 100644
--- a/apps/dev-playground/server/index.ts
+++ b/apps/dev-playground/server/index.ts
@@ -1,5 +1,12 @@
import "reflect-metadata";
-import { analytics, createApp, files, genie, server } from "@databricks/appkit";
+import {
+ analytics,
+ createApp,
+ files,
+ genie,
+ server,
+ serving,
+} from "@databricks/appkit";
import { WorkspaceClient } from "@databricks/sdk-experimental";
import { lakebaseExamples } from "./lakebase-examples-plugin";
import { reconnect } from "./reconnect-plugin";
@@ -26,6 +33,7 @@ createApp({
}),
lakebaseExamples(),
files(),
+ serving(),
],
...(process.env.APPKIT_E2E_TEST && { client: createMockClient() }),
}).then((appkit) => {
diff --git a/docs/docs/plugins/serving.md b/docs/docs/plugins/serving.md
new file mode 100644
index 00000000..4b2d7a54
--- /dev/null
+++ b/docs/docs/plugins/serving.md
@@ -0,0 +1,213 @@
+---
+sidebar_position: 7
+---
+
+# Serving plugin
+
+Provides an authenticated proxy to [Databricks Model Serving](https://docs.databricks.com/aws/en/machine-learning/model-serving) endpoints, with invoke and streaming support.
+
+**Key features:**
+- Named endpoint aliases for multiple serving endpoints
+- Non-streaming (`invoke`) and SSE streaming (`stream`) invocation
+- Automatic OpenAPI type generation for request/response schemas
+- Request body filtering based on endpoint schema
+- On-behalf-of (OBO) user execution
+
+## Basic usage
+
+```ts
+import { createApp, server, serving } from "@databricks/appkit";
+
+await createApp({
+ plugins: [
+ server(),
+ serving(),
+ ],
+});
+```
+
+With no configuration, the plugin reads `DATABRICKS_SERVING_ENDPOINT` from the environment and registers it under the `default` alias.
+
+## Configuration options
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `endpoints` | `Record` | `{ default: { env: "DATABRICKS_SERVING_ENDPOINT" } }` | Map of alias names to endpoint configs |
+| `timeout` | `number` | `120000` | Request timeout in ms |
+
+### Endpoint aliases
+
+Endpoint aliases let you reference multiple serving endpoints by name:
+
+```ts
+serving({
+ endpoints: {
+ llm: { env: "DATABRICKS_SERVING_ENDPOINT" },
+ classifier: { env: "DATABRICKS_SERVING_ENDPOINT_CLASSIFIER" },
+ },
+})
+```
+
+Each alias maps to an environment variable holding the actual endpoint name. If an endpoint serves multiple models, you can use `servedModel` to bypass traffic routing and target a specific model directly:
+
+```ts
+serving({
+ endpoints: {
+ llm: { env: "DATABRICKS_SERVING_ENDPOINT", servedModel: "llama-v2" },
+ },
+})
+```
+
+## Type generation
+
+The `appKitServingTypesPlugin()` Vite plugin generates TypeScript types from your serving endpoints' OpenAPI schemas. Add it to your `vite.config.ts`:
+
+```ts
+import { appKitServingTypesPlugin } from "@databricks/appkit";
+
+export default defineConfig({
+ plugins: [
+ appKitServingTypesPlugin(),
+ ],
+});
+```
+
+The plugin auto-discovers endpoint configuration from your server file (`server/index.ts` or `server/server.ts`) — no manual config passing needed.
+
+Generated types provide:
+- **Alias autocomplete** in both backend (`AppKit.serving("alias")`) and frontend hooks (`useServingStream`, `useServingInvoke`)
+- **Typed request/response/chunk** per endpoint based on OpenAPI schemas
+
+If an endpoint's OpenAPI schema is unavailable (not deployed, env var not set), the plugin generates generic fallback types. The endpoint is still usable — just without typed request/response.
+
+:::note
+Endpoints that don't define a streaming response schema in their OpenAPI spec will have `chunk: unknown`. For these endpoints, use `useServingInvoke` instead of `useServingStream` — the `response` type will still be properly typed.
+:::
+
+## Environment variables
+
+| Variable | Description |
+|----------|-------------|
+| `DATABRICKS_SERVING_ENDPOINT` | Default endpoint name (used when `endpoints` config is omitted) |
+
+When using named endpoints, define a custom environment variable per alias (e.g. `DATABRICKS_SERVING_ENDPOINT_CLASSIFIER`).
+
+## HTTP endpoints
+
+### Named mode (with `endpoints` config)
+
+- `POST /api/serving/:alias/invoke` — Non-streaming invocation
+- `POST /api/serving/:alias/stream` — SSE streaming invocation
+
+### Default mode (no `endpoints` config)
+
+- `POST /api/serving/invoke` — Non-streaming invocation
+- `POST /api/serving/stream` — SSE streaming invocation
+
+### Request format
+
+```
+POST /api/serving/:alias/invoke
+Content-Type: application/json
+
+{
+ "messages": [
+ { "role": "user", "content": "Hello" }
+ ]
+}
+```
+
+## Programmatic access
+
+The plugin exports `invoke` and `stream` methods for server-side use:
+
+```ts
+const AppKit = await createApp({
+ plugins: [
+ server(),
+ serving({
+ endpoints: {
+ llm: { env: "DATABRICKS_SERVING_ENDPOINT" },
+ },
+ }),
+ ],
+});
+
+// Non-streaming
+const result = await AppKit.serving("llm").invoke({
+ messages: [{ role: "user", content: "Hello" }],
+});
+
+// Streaming
+for await (const chunk of AppKit.serving("llm").stream({
+ messages: [{ role: "user", content: "Hello" }],
+})) {
+ console.log(chunk);
+}
+```
+
+## Frontend hooks
+
+The `@databricks/appkit-ui` package provides React hooks for serving endpoints:
+
+### useServingStream
+
+Streaming invocation via SSE:
+
+```tsx
+import { useServingStream } from "@databricks/appkit-ui/react";
+
+function ChatStream() {
+ const { stream, chunks, streaming, error, reset } = useServingStream(
+ { messages: [{ role: "user", content: "Hello" }] },
+ {
+ alias: "llm",
+ onComplete: (finalChunks) => {
+ // Called with all accumulated chunks when the stream finishes
+ console.log("Stream done, got", finalChunks.length, "chunks");
+ },
+ },
+ );
+
+ return (
+ <>
+
+
+ {chunks.map((chunk, i) => {JSON.stringify(chunk)})}
+ {error && {error}
}
+ >
+ );
+}
+```
+
+### useServingInvoke
+
+Non-streaming invocation. `invoke()` returns a promise with the response data (or `null` on error):
+
+```tsx
+import { useServingInvoke } from "@databricks/appkit-ui/react";
+
+function Classify() {
+ const { invoke, data, loading, error } = useServingInvoke(
+ { inputs: ["sample text"] },
+ { alias: "classifier" },
+ );
+
+ async function handleClick() {
+ const result = await invoke();
+ if (result) {
+ console.log("Classification result:", result);
+ }
+ }
+
+ return (
+ <>
+
+ {data && {JSON.stringify(data)}}
+ {error && {error}
}
+ >
+ );
+}
+```
+
+Both hooks accept `autoStart: true` to invoke automatically on mount.
diff --git a/template/appkit.plugins.json b/template/appkit.plugins.json
index cf60a8af..c21d8e80 100644
--- a/template/appkit.plugins.json
+++ b/template/appkit.plugins.json
@@ -149,6 +149,30 @@
"optional": []
},
"requiredByTemplate": true
+ },
+ "serving": {
+ "name": "serving",
+ "displayName": "Model Serving Plugin",
+ "description": "Authenticated proxy to Databricks Model Serving endpoints",
+ "package": "@databricks/appkit",
+ "resources": {
+ "required": [
+ {
+ "type": "serving_endpoint",
+ "alias": "Serving Endpoint",
+ "resourceKey": "serving-endpoint",
+ "description": "Model Serving endpoint for inference",
+ "permission": "CAN_QUERY",
+ "fields": {
+ "name": {
+ "env": "DATABRICKS_SERVING_ENDPOINT",
+ "description": "Serving endpoint name"
+ }
+ }
+ }
+ ],
+ "optional": []
+ }
}
}
}
diff --git a/template/client/src/App.tsx b/template/client/src/App.tsx
index fb4c28e6..a94bb5bc 100644
--- a/template/client/src/App.tsx
+++ b/template/client/src/App.tsx
@@ -17,6 +17,9 @@ import { GeniePage } from './pages/genie/GeniePage';
{{- if .plugins.files}}
import { FilesPage } from './pages/files/FilesPage';
{{- end}}
+{{- if .plugins.serving}}
+import { ServingPage } from './pages/serving/ServingPage';
+{{- end}}
const navLinkClass = ({ isActive }: { isActive: boolean }) =>
`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
@@ -53,6 +56,11 @@ function Layout() {
Files
+{{- end}}
+{{- if .plugins.serving}}
+
+ Serving
+
{{- end}}
@@ -80,6 +88,9 @@ const router = createBrowserRouter([
{{- end}}
{{- if .plugins.files}}
{ path: '/files', element: },
+{{- end}}
+{{- if .plugins.serving}}
+ { path: '/serving', element: },
{{- end}}
],
},
diff --git a/template/client/src/pages/serving/ServingPage.tsx b/template/client/src/pages/serving/ServingPage.tsx
new file mode 100644
index 00000000..b80934ba
--- /dev/null
+++ b/template/client/src/pages/serving/ServingPage.tsx
@@ -0,0 +1,127 @@
+{{if .plugins.serving -}}
+import { useServingInvoke } from '@databricks/appkit-ui/react';
+// For streaming endpoints (e.g. chat models), use useServingStream instead:
+// import { useServingStream } from '@databricks/appkit-ui/react';
+import { useState } from 'react';
+
+interface ChatChoice {
+ message?: { content?: string };
+}
+
+interface ChatResponse {
+ choices?: ChatChoice[];
+}
+
+function extractContent(data: unknown): string {
+ const resp = data as ChatResponse;
+ return resp?.choices?.[0]?.message?.content ?? JSON.stringify(data);
+}
+
+interface Message {
+ id: string;
+ role: 'user' | 'assistant';
+ content: string;
+}
+
+export function ServingPage() {
+ const [input, setInput] = useState('');
+ const [messages, setMessages] = useState([]);
+
+ const { invoke, loading, error } = useServingInvoke({ messages: [] });
+ // For streaming endpoints (e.g. chat models), use useServingStream instead:
+ // const { stream, chunks, streaming, error, reset } = useServingStream({ messages: [] });
+ // Then accumulate chunks: chunks.map(c => c?.choices?.[0]?.delta?.content ?? '').join('')
+
+ function handleSubmit(e: React.FormEvent) {
+ e.preventDefault();
+ if (!input.trim() || loading) return;
+
+ const userMessage: Message = {
+ id: crypto.randomUUID(),
+ role: 'user',
+ content: input.trim(),
+ };
+
+ const fullMessages = [
+ ...messages.map(({ role, content }) => ({ role, content })),
+ { role: 'user' as const, content: userMessage.content },
+ ];
+
+ setMessages((prev) => [...prev, userMessage]);
+ setInput('');
+
+ void invoke({ messages: fullMessages }).then((result) => {
+ if (result) {
+ setMessages((prev) => [
+ ...prev,
+ { id: crypto.randomUUID(), role: 'assistant', content: extractContent(result) },
+ ]);
+ }
+ });
+ }
+
+ return (
+
+
+
Model Serving
+
+ Chat with a Databricks Model Serving endpoint.
+
+
+
+
+
+ {messages.map((msg) => (
+
+ ))}
+
+ {loading && (
+
+ )}
+
+ {error && (
+
+ Error: {error}
+
+ )}
+
+
+
+
+
+ );
+}
+{{- end}}
diff --git a/template/client/vite.config.ts b/template/client/vite.config.ts
index b49d4055..12c1d864 100644
--- a/template/client/vite.config.ts
+++ b/template/client/vite.config.ts
@@ -2,11 +2,20 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
import path from 'node:path';
+{{- if .plugins.serving}}
+import { appKitServingTypesPlugin } from '@databricks/appkit';
+{{- end}}
// https://vite.dev/config/
export default defineConfig({
root: __dirname,
- plugins: [react(), tailwindcss()],
+ plugins: [
+ react(),
+ tailwindcss(),
+{{- if .plugins.serving}}
+ appKitServingTypesPlugin(),
+{{- end}}
+ ],
server: {
middlewareMode: true,
},
diff --git a/template/databricks.yml.tmpl b/template/databricks.yml.tmpl
index accf7709..77997d31 100644
--- a/template/databricks.yml.tmpl
+++ b/template/databricks.yml.tmpl
@@ -13,7 +13,7 @@ resources:
description: "{{.appDescription}}"
source_code_path: ./
-{{- if or .plugins.genie .plugins.files}}
+{{- if or .plugins.genie .plugins.files .plugins.serving}}
user_api_scopes:
{{- if .plugins.genie}}
- dashboards.genie
@@ -21,8 +21,11 @@ resources:
{{- if .plugins.files}}
- files.files
{{- end}}
+{{- if .plugins.serving}}
+ - serving.serving-endpoints
+{{- end}}
{{- else}}
- # Uncomment to enable on behalf of user API scopes. Available scopes: sql, dashboards.genie, files.files
+ # Uncomment to enable on behalf of user API scopes. Available scopes: sql, dashboards.genie, files.files, serving.serving-endpoints
# user_api_scopes:
# - sql
{{- end}}
diff --git a/tools/generate-app-templates.ts b/tools/generate-app-templates.ts
index 4b029121..1eff9357 100644
--- a/tools/generate-app-templates.ts
+++ b/tools/generate-app-templates.ts
@@ -55,21 +55,23 @@ const FEATURE_DEPENDENCIES: Record = {
files: "Volume",
genie: "Genie Space",
lakebase: "Database",
+ serving: "Serving Endpoint",
};
const APP_TEMPLATES: AppTemplate[] = [
{
name: "appkit-all-in-one",
- features: ["analytics", "files", "genie", "lakebase"],
+ features: ["analytics", "files", "genie", "lakebase", "serving"],
set: {
"analytics.sql-warehouse.id": "placeholder",
"files.files.path": "placeholder",
"genie.genie-space.id": "placeholder",
"lakebase.postgres.branch": "placeholder",
"lakebase.postgres.database": "placeholder",
+ "serving.serving-endpoint.name": "placeholder",
},
description:
- "Full-stack Node.js app with SQL analytics dashboards, file browser, Genie AI conversations, and Lakebase Autoscaling (Postgres) CRUD",
+ "Full-stack Node.js app with SQL analytics dashboards, file browser, Genie AI conversations, Lakebase Autoscaling (Postgres) CRUD, and Model Serving",
},
{
name: "appkit-analytics",
@@ -96,6 +98,15 @@ const APP_TEMPLATES: AppTemplate[] = [
},
description: "Node.js app with file browser for Databricks Volumes",
},
+ {
+ name: "appkit-serving",
+ features: ["serving"],
+ set: {
+ "serving.serving-endpoint.name": "placeholder",
+ },
+ description:
+ "Node.js app with Databricks Model Serving endpoint integration",
+ },
{
name: "appkit-lakebase",
features: ["lakebase"],