From d5cc4338f3860e2f7b91c111631e8c08713b7984 Mon Sep 17 00:00:00 2001 From: Adam Gurary Date: Tue, 11 Aug 2026 23:03:55 -0700 Subject: [PATCH 01/39] feat: publish traced AppKit agent templates Signed-off-by: Adam Gurary --- appkit-agents/.env.example | 7 + appkit-agents/.env.tmpl | 20 + appkit-agents/.gitignore | 14 + appkit-agents/.prettierignore | 36 + appkit-agents/.prettierrc.json | 12 + appkit-agents/README.md | 180 + appkit-agents/app.yaml | 18 + appkit-agents/appkit.plugins.json | 441 + appkit-agents/client/components.json | 21 + appkit-agents/client/index.html | 18 + appkit-agents/client/postcss.config.js | 6 + .../client/public/apple-touch-icon.png | Bin 0 -> 2547 bytes appkit-agents/client/public/favicon-16x16.png | Bin 0 -> 302 bytes .../client/public/favicon-192x192.png | Bin 0 -> 2762 bytes appkit-agents/client/public/favicon-32x32.png | Bin 0 -> 492 bytes appkit-agents/client/public/favicon-48x48.png | Bin 0 -> 686 bytes .../client/public/favicon-512x512.png | Bin 0 -> 10325 bytes appkit-agents/client/public/favicon.svg | 6 + appkit-agents/client/public/site.webmanifest | 19 + appkit-agents/client/src/App.tsx | 144 + appkit-agents/client/src/ErrorBoundary.tsx | 75 + appkit-agents/client/src/index.css | 82 + appkit-agents/client/src/lib/utils.ts | 6 + appkit-agents/client/src/main.tsx | 27 + .../client/src/pages/agents/AgentChat.tsx | 232 + appkit-agents/client/src/vite-env.d.ts | 1 + appkit-agents/client/tailwind.config.ts | 10 + appkit-agents/client/vite.config.ts | 26 + appkit-agents/config/agents/planner/agent.md | 36 + appkit-agents/databricks.yml | 67 + appkit-agents/databricks.yml.tmpl | 77 + appkit-agents/eslint.config.js | 89 + appkit-agents/package.json | 81 + appkit-agents/server/agents/helper.ts | 57 + appkit-agents/server/server.ts | 13 + appkit-agents/tsconfig.client.json | 28 + appkit-agents/tsconfig.json | 4 + appkit-agents/tsconfig.server.json | 12 + appkit-agents/tsconfig.shared.json | 21 + appkit-agents/tsdown.server.config.ts | 11 + appkit-agents/vitest.config.ts | 16 + appkit-all-in-one/.env.example | 4 + appkit-all-in-one/.env.tmpl | 7 + appkit-all-in-one/.gitignore | 1 + appkit-all-in-one/CLAUDE.md | 18 - appkit-all-in-one/README.md | 28 +- appkit-all-in-one/app.yaml | 18 + appkit-all-in-one/appkit.plugins.json | 74 +- appkit-all-in-one/client/src/App.tsx | 12 +- appkit-all-in-one/client/src/main.tsx | 16 +- .../client/src/pages/agents/AgentChat.tsx | 232 + .../client/src/pages/serving/ServingPage.tsx | 125 + appkit-all-in-one/client/vite.config.ts | 2 +- .../config/agents/planner/agent.md | 36 + .../config/metric-views/definitions.json | 4 + appkit-all-in-one/databricks.yml | 52 +- appkit-all-in-one/databricks.yml.tmpl | 33 +- appkit-all-in-one/eslint.config.js | 2 - appkit-all-in-one/manifest.yaml | 26 - appkit-all-in-one/package-lock.json | 15229 ---------------- appkit-all-in-one/package.json | 27 +- appkit-all-in-one/playwright.config.ts | 29 - appkit-all-in-one/server/agents/helper.ts | 57 + .../server/routes/lakebase/todo-routes.ts | 4 +- appkit-all-in-one/server/server.ts | 9 +- appkit-all-in-one/tests/smoke.spec.ts | 156 - appkit-all-in-one/vitest.config.ts | 2 +- 67 files changed, 2610 insertions(+), 15506 deletions(-) create mode 100644 appkit-agents/.env.example create mode 100644 appkit-agents/.env.tmpl create mode 100644 appkit-agents/.gitignore create mode 100644 appkit-agents/.prettierignore create mode 100644 appkit-agents/.prettierrc.json create mode 100644 appkit-agents/README.md create mode 100644 appkit-agents/app.yaml create mode 100644 appkit-agents/appkit.plugins.json create mode 100644 appkit-agents/client/components.json create mode 100644 appkit-agents/client/index.html create mode 100644 appkit-agents/client/postcss.config.js create mode 100644 appkit-agents/client/public/apple-touch-icon.png create mode 100644 appkit-agents/client/public/favicon-16x16.png create mode 100644 appkit-agents/client/public/favicon-192x192.png create mode 100644 appkit-agents/client/public/favicon-32x32.png create mode 100644 appkit-agents/client/public/favicon-48x48.png create mode 100644 appkit-agents/client/public/favicon-512x512.png create mode 100644 appkit-agents/client/public/favicon.svg create mode 100644 appkit-agents/client/public/site.webmanifest create mode 100644 appkit-agents/client/src/App.tsx create mode 100644 appkit-agents/client/src/ErrorBoundary.tsx create mode 100644 appkit-agents/client/src/index.css create mode 100644 appkit-agents/client/src/lib/utils.ts create mode 100644 appkit-agents/client/src/main.tsx create mode 100644 appkit-agents/client/src/pages/agents/AgentChat.tsx create mode 100644 appkit-agents/client/src/vite-env.d.ts create mode 100644 appkit-agents/client/tailwind.config.ts create mode 100644 appkit-agents/client/vite.config.ts create mode 100644 appkit-agents/config/agents/planner/agent.md create mode 100644 appkit-agents/databricks.yml create mode 100644 appkit-agents/databricks.yml.tmpl create mode 100644 appkit-agents/eslint.config.js create mode 100644 appkit-agents/package.json create mode 100644 appkit-agents/server/agents/helper.ts create mode 100644 appkit-agents/server/server.ts create mode 100644 appkit-agents/tsconfig.client.json create mode 100644 appkit-agents/tsconfig.json create mode 100644 appkit-agents/tsconfig.server.json create mode 100644 appkit-agents/tsconfig.shared.json create mode 100644 appkit-agents/tsdown.server.config.ts create mode 100644 appkit-agents/vitest.config.ts delete mode 100644 appkit-all-in-one/CLAUDE.md create mode 100644 appkit-all-in-one/client/src/pages/agents/AgentChat.tsx create mode 100644 appkit-all-in-one/client/src/pages/serving/ServingPage.tsx create mode 100644 appkit-all-in-one/config/agents/planner/agent.md create mode 100644 appkit-all-in-one/config/metric-views/definitions.json delete mode 100644 appkit-all-in-one/manifest.yaml delete mode 100644 appkit-all-in-one/package-lock.json delete mode 100644 appkit-all-in-one/playwright.config.ts create mode 100644 appkit-all-in-one/server/agents/helper.ts delete mode 100644 appkit-all-in-one/tests/smoke.spec.ts diff --git a/appkit-agents/.env.example b/appkit-agents/.env.example new file mode 100644 index 00000000..f9787b69 --- /dev/null +++ b/appkit-agents/.env.example @@ -0,0 +1,7 @@ +DATABRICKS_HOST=https://... +MLFLOW_EXPERIMENT_ID=your_mlflow_experiment_id +MLFLOW_TRACING_SQL_WAREHOUSE_ID=your_mlflow_tracing_warehouse_id +# DATABRICKS_SERVING_ENDPOINT_NAME=your_agents_serving_endpoint_name +DATABRICKS_APP_PORT=8000 +DATABRICKS_APP_NAME=appkit-agents +FLASK_RUN_HOST=0.0.0.0 diff --git a/appkit-agents/.env.tmpl b/appkit-agents/.env.tmpl new file mode 100644 index 00000000..91a24004 --- /dev/null +++ b/appkit-agents/.env.tmpl @@ -0,0 +1,20 @@ +# This file is a Go template processed by `databricks apps init --template `. +# Template variables are substituted and the result is written as .env. +{{- if ne .profile ""}} +DATABRICKS_CONFIG_PROFILE={{.profile}} +{{- else}} +DATABRICKS_HOST={{.workspaceHost}} +{{- end}} +{{- if .dotEnv.content}} +{{.dotEnv.content}} +{{- end}} +{{- if .plugins.agents}} +# `npm run setup` replaces these defaults after provisioning the immutable location. +MLFLOW_UC_CATALOG=main +MLFLOW_UC_SCHEMA=agent_traces +MLFLOW_UC_TABLE_PREFIX=appkit +MLFLOW_OTEL_SPANS_TABLE=main.agent_traces.appkit_otel_spans +{{- end}} +DATABRICKS_APP_PORT=8000 +DATABRICKS_APP_NAME={{.projectName}} +FLASK_RUN_HOST=localhost diff --git a/appkit-agents/.gitignore b/appkit-agents/.gitignore new file mode 100644 index 00000000..29895e7c --- /dev/null +++ b/appkit-agents/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +client/dist/ +dist/ +build/ +.env +.databricks/ +.smoke-test/ +test-results/ +playwright-report/ + +# Auto-generated types (endpoint-specific, varies per developer) +shared/appkit-types/serving.d.ts + diff --git a/appkit-agents/.prettierignore b/appkit-agents/.prettierignore new file mode 100644 index 00000000..7d3d77c6 --- /dev/null +++ b/appkit-agents/.prettierignore @@ -0,0 +1,36 @@ +# Dependencies +node_modules + +# Build outputs +dist +build +client/dist +.next +.databricks/ + +# Environment files +.env +.env.local +.env.*.local + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Coverage +coverage + +# Cache +.cache +.turbo + +# Lock files +package-lock.json +yarn.lock +pnpm-lock.yaml + +# Vendor +vendor diff --git a/appkit-agents/.prettierrc.json b/appkit-agents/.prettierrc.json new file mode 100644 index 00000000..d95a63f6 --- /dev/null +++ b/appkit-agents/.prettierrc.json @@ -0,0 +1,12 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 120, + "tabWidth": 2, + "useTabs": false, + "arrowParens": "always", + "endOfLine": "lf", + "bracketSpacing": true, + "jsxSingleQuote": false +} diff --git a/appkit-agents/README.md b/appkit-agents/README.md new file mode 100644 index 00000000..321172b2 --- /dev/null +++ b/appkit-agents/README.md @@ -0,0 +1,180 @@ +# appkit-agents + +A Databricks App powered by [AppKit](https://developers.databricks.com/docs/appkit/v0/), featuring React, TypeScript, and Tailwind CSS. + +**Enabled plugins:** +- **Server** -- Express HTTP server with static file serving and Vite dev mode + +## Prerequisites + +- Node.js v22+ and npm +- Databricks CLI (for deployment) +- Access to a Databricks workspace + +## Databricks Authentication + +### Local Development + +For local development, configure your environment variables by creating a `.env` file: + +```bash +cp .env.example .env +``` + +Edit `.env` and set the environment variables you need: + +```env +DATABRICKS_HOST=https://your-workspace.cloud.databricks.com +DATABRICKS_APP_PORT=8000 +# ... other environment variables, depending on the plugins you use +``` + +### CLI Authentication + +The Databricks CLI requires authentication to deploy and manage apps. Configure authentication using one of these methods: + +#### OAuth U2M + +Interactive browser-based authentication with short-lived tokens: + +```bash +databricks auth login --host https://your-workspace.cloud.databricks.com +``` + +This will open your browser to complete authentication. The CLI saves credentials to `~/.databrickscfg`. + +#### Configuration Profiles + +Use multiple profiles for different workspaces: + +```ini +[DEFAULT] +host = https://dev-workspace.cloud.databricks.com + +[production] +host = https://prod-workspace.cloud.databricks.com +client_id = prod-client-id +client_secret = prod-client-secret +``` + +Deploy using a specific profile: + +```bash +databricks bundle deploy --profile production +``` + +**Note:** Personal Access Tokens (PATs) are legacy authentication. OAuth is strongly recommended for better security. + +## Getting Started + +### Install Dependencies + +```bash +npm install +``` + +### Development + +Run the app in development mode with hot reload: + +```bash +npm run dev +``` + +The app will be available at the URL shown in the console output. + +### Build + +Build both client and server for production: + +```bash +npm run build +``` + +This creates: + +- `dist/server.js` - Compiled server bundle +- `client/dist/` - Bundled client assets + +### Production + +Run the production build: + +```bash +npm start +``` + +## Code Quality + +There are a few commands to help you with code quality: + +```bash +# Type checking +npm run typecheck + +# Linting +npm run lint +npm run lint:fix + +# Formatting +npm run format +npm run format:fix +``` + +## Deployment with Databricks Asset Bundles + +### 1. Configure Bundle + +Update `databricks.yml` with your workspace settings: + +```yaml +targets: + default: + workspace: + host: https://your-workspace.cloud.databricks.com +``` + +Make sure to replace all placeholder values in `databricks.yml` with your actual resource IDs. + +### 2. Deploy + +Deploy and start the app with a single command: + +```bash +databricks apps deploy +``` + +`databricks apps deploy` validates the project, deploys it, starts the app, and prints its URL. + +### Deploy to Production + +1. Configure the production target in `databricks.yml` +2. Deploy to production: + +```bash +databricks apps deploy -t prod +``` + +> **Restarting a stopped app:** apps stop after a period of inactivity. To start one again without redeploying, run `databricks apps start `. + +## Project Structure + +``` +* client/ # React frontend + * src/ # Source code + * public/ # Static assets +* server/ # Express backend + * server.ts # Server entry point + * routes/ # Routes +* shared/ # Shared types +* databricks.yml # Bundle configuration +* app.yaml # App configuration +* .env.example # Environment variables example +``` + +## Tech Stack + +- **Backend**: Node.js, Express +- **Frontend**: React.js, TypeScript, Vite, Tailwind CSS, React Router +- **UI Components**: Radix UI, shadcn/ui +- **Databricks**: AppKit SDK diff --git a/appkit-agents/app.yaml b/appkit-agents/app.yaml new file mode 100644 index 00000000..45be9c8c --- /dev/null +++ b/appkit-agents/app.yaml @@ -0,0 +1,18 @@ +command: ['npm', 'run', 'start'] +env: + - name: MLFLOW_EXPERIMENT_ID + valueFrom: mlflow-experiment + - name: MLFLOW_TRACING_SQL_WAREHOUSE_ID + valueFrom: mlflow-tracing-warehouse + - name: DATABRICKS_SERVING_ENDPOINT_NAME + valueFrom: agents-serving-endpoint + # UC coordinates are runtime configuration, not fields on the experiment + # resource binding. `npm run setup` replaces these defaults after provisioning. + - name: MLFLOW_UC_CATALOG + value: main + - name: MLFLOW_UC_SCHEMA + value: agent_traces + - name: MLFLOW_UC_TABLE_PREFIX + value: appkit + - name: MLFLOW_OTEL_SPANS_TABLE + value: main.agent_traces.appkit_otel_spans diff --git a/appkit-agents/appkit.plugins.json b/appkit-agents/appkit.plugins.json new file mode 100644 index 00000000..f8784afc --- /dev/null +++ b/appkit-agents/appkit.plugins.json @@ -0,0 +1,441 @@ +{ + "$schema": "https://databricks.github.io/appkit/schemas/template-plugins.schema.json", + "version": "2.0", + "plugins": { + "agents": { + "name": "agents", + "displayName": "Agents Plugin", + "description": "AI agents driven by markdown configs or code, with auto-tool-discovery from registered plugins", + "package": "@databricks/appkit", + "resources": { + "required": [ + { + "type": "experiment", + "alias": "MLflow agent trace experiment", + "resourceKey": "mlflow-experiment", + "description": "MLflow experiment bound to immutable Unity Catalog trace tables", + "permission": "CAN_MANAGE", + "fields": { + "id": { + "env": "MLFLOW_EXPERIMENT_ID", + "description": "MLflow experiment ID", + "origin": "user" + } + } + }, + { + "type": "sql_warehouse", + "alias": "MLflow tracing SQL warehouse", + "resourceKey": "mlflow-tracing-warehouse", + "description": "SQL warehouse used to provision and query MLflow UC trace tables", + "permission": "CAN_USE", + "fields": { + "id": { + "env": "MLFLOW_TRACING_SQL_WAREHOUSE_ID", + "description": "SQL warehouse ID for MLflow UC tracing", + "discovery": { + "type": "kind", + "resourceKind": "warehouse" + }, + "origin": "user" + } + } + } + ], + "optional": [ + { + "type": "serving_endpoint", + "alias": "Default LLM for agents", + "resourceKey": "agents-serving-endpoint", + "description": "Default streaming-capable LLM endpoint for agents that don't pin their own model", + "permission": "CAN_QUERY", + "fields": { + "name": { + "env": "DATABRICKS_SERVING_ENDPOINT_NAME", + "description": "Default LLM serving endpoint name", + "origin": "user" + } + } + } + ] + }, + "stability": "beta" + }, + "aiSearch": { + "name": "aiSearch", + "displayName": "AI Search Plugin", + "description": "Query Databricks Vector Search indexes with built-in hybrid search, reranking, and pagination", + "package": "@databricks/appkit", + "resources": { + "required": [ + { + "type": "vector_search_index", + "alias": "Vector Search Index", + "resourceKey": "vector-search-index", + "description": "A Databricks Vector Search index to query. Index names configured via plugin config.", + "permission": "SELECT", + "fields": { + "id": { + "env": "DATABRICKS_VS_INDEX_NAME", + "description": "Three-level UC name of the default index (catalog.schema.index_name)", + "origin": "user" + } + } + } + ], + "optional": [] + }, + "stability": "beta", + "scaffolding": { + "rules": { + "must": [ + "List the vector search index's source-table columns (unity-catalog tables API) so the user can pick which to return", + "Ask the user which of those columns the search should return", + "Write aiSearch({ indexes: { default: { columns: [...] } } }) in the server file; the client queries the 'default' alias" + ] + } + } + }, + "analytics": { + "name": "analytics", + "displayName": "Analytics Plugin", + "description": "SQL query execution against Databricks SQL Warehouses", + "package": "@databricks/appkit", + "resources": { + "required": [ + { + "type": "sql_warehouse", + "alias": "SQL Warehouse", + "resourceKey": "sql-warehouse", + "description": "SQL Warehouse for executing analytics queries", + "permission": "CAN_USE", + "fields": { + "id": { + "env": "DATABRICKS_WAREHOUSE_ID", + "description": "SQL Warehouse ID", + "discovery": { + "type": "kind", + "resourceKind": "warehouse" + }, + "origin": "user" + } + } + } + ], + "optional": [] + }, + "scaffolding": { + "rules": { + "must": [ + "Before init, ensure the SQL Warehouse passed via --set analytics.sql-warehouse.id is running" + ], + "should": [ + "After init, ensure config/queries/ has at least one .sql file before running npm run typegen" + ] + } + } + }, + "files": { + "name": "files", + "displayName": "Files Plugin", + "description": "File operations against Databricks Volumes and Unity Catalog", + "package": "@databricks/appkit", + "resources": { + "required": [ + { + "type": "volume", + "alias": "Files", + "resourceKey": "files", + "description": "Permission to write to volumes", + "permission": "WRITE_VOLUME", + "fields": { + "path": { + "env": "DATABRICKS_VOLUME_FILES", + "description": "Volume path for file storage (e.g. /Volumes/catalog/schema/volume_name)", + "discovery": { + "type": "kind", + "resourceKind": "volume", + "select": "full_name" + }, + "origin": "user" + } + } + } + ], + "optional": [] + }, + "scaffolding": { + "rules": { + "must": [ + "Before init, verify your Unity Catalog volume exists and you have WRITE_VOLUME permission" + ] + } + } + }, + "genie": { + "name": "genie", + "displayName": "Genie Plugin", + "description": "AI/BI Genie space integration for natural language data queries", + "package": "@databricks/appkit", + "resources": { + "required": [ + { + "type": "genie_space", + "alias": "Genie Space", + "resourceKey": "genie-space", + "description": "Genie Space for AI-powered data queries. Space IDs configured via plugin config.", + "permission": "CAN_RUN", + "fields": { + "id": { + "env": "DATABRICKS_GENIE_SPACE_ID", + "description": "Default Genie Space ID", + "discovery": { + "type": "kind", + "resourceKind": "genie_space" + }, + "origin": "user" + }, + "name": { + "description": "Genie Space display name", + "origin": "user" + } + } + } + ], + "optional": [] + }, + "scaffolding": { + "rules": { + "must": [ + "After init, configure the 'spaces' map in plugin config with alias-to-Space-ID mappings" + ] + } + } + }, + "jobs": { + "name": "jobs", + "displayName": "Jobs Plugin", + "description": "Manage Databricks Lakeflow Jobs.", + "package": "@databricks/appkit", + "resources": { + "required": [ + { + "type": "job", + "alias": "Job", + "resourceKey": "job", + "description": "A Databricks job to trigger and monitor", + "permission": "CAN_MANAGE_RUN", + "fields": { + "id": { + "env": "DATABRICKS_JOB_ID", + "description": "Numeric Databricks job ID. Find it in the Jobs UI or via `databricks jobs list`.", + "origin": "user" + } + } + } + ], + "optional": [] + } + }, + "lakebase": { + "name": "lakebase", + "displayName": "Lakebase", + "description": "SQL query execution against Databricks Lakebase Autoscaling", + "package": "@databricks/appkit", + "resources": { + "required": [ + { + "type": "postgres", + "alias": "Postgres", + "resourceKey": "postgres", + "description": "Lakebase Postgres database for persistent storage", + "permission": "CAN_CONNECT_AND_CREATE", + "fields": { + "project": { + "description": "Full Lakebase Postgres project resource name. Obtain by running `databricks postgres list-projects`, select the desired item from the output array and use its .name value.", + "examples": [ + "projects/{project-id}" + ], + "discovery": { + "type": "kind", + "resourceKind": "postgres_project", + "select": "name" + }, + "origin": "user" + }, + "branch": { + "description": "Full Lakebase Postgres branch resource name. Obtain by running `databricks postgres list-branches {project-name}`, select the desired item from the output array and use its .name value. Requires the project resource name.", + "examples": [ + "projects/{project-id}/branches/{branch-id}" + ], + "discovery": { + "type": "kind", + "resourceKind": "postgres_branch", + "select": "name", + "dependsOn": "project" + }, + "origin": "user" + }, + "database": { + "description": "Full Lakebase Postgres database resource name. Obtain by running `databricks postgres list-databases {branch-name}`, select the desired item from the output array and use its .name value. Requires the branch resource name.", + "examples": [ + "projects/{project-id}/branches/{branch-id}/databases/{database-id}" + ], + "discovery": { + "type": "kind", + "resourceKind": "postgres_database", + "select": "name", + "dependsOn": "branch" + }, + "origin": "user" + }, + "host": { + "env": "PGHOST", + "description": "Postgres host for local development. Auto-injected by the platform at deploy time.", + "localOnly": true, + "resolve": "postgres:host", + "origin": "platform" + }, + "databaseName": { + "env": "PGDATABASE", + "description": "Postgres database name for local development. Auto-injected by the platform at deploy time.", + "localOnly": true, + "resolve": "postgres:databaseName", + "origin": "platform" + }, + "endpointPath": { + "env": "LAKEBASE_ENDPOINT", + "description": "Lakebase endpoint resource name. Auto-injected at runtime via app.yaml valueFrom: postgres. For local development, obtain by running `databricks postgres list-endpoints {branch-name}`, select the desired item from the output array and use its .name value.", + "bundleIgnore": true, + "examples": [ + "projects/{project-id}/branches/{branch-id}/endpoints/{endpoint-id}" + ], + "resolve": "postgres:endpointPath", + "origin": "cli" + }, + "port": { + "env": "PGPORT", + "description": "Postgres port. Auto-injected by the platform at deploy time.", + "localOnly": true, + "value": "5432", + "origin": "platform" + }, + "sslmode": { + "env": "PGSSLMODE", + "description": "Postgres SSL mode. Auto-injected by the platform at deploy time.", + "localOnly": true, + "value": "require", + "origin": "platform" + } + } + } + ], + "optional": [] + }, + "scaffolding": { + "rules": { + "should": [ + "After init, run any database migrations for your chosen ORM before first request", + "After init, verify Lakebase connectivity with 'psql $PGHOST -c \"select 1\"'" + ] + } + } + }, + "server": { + "name": "server", + "displayName": "Server Plugin", + "description": "HTTP server with Express, static file serving, and Vite dev mode support", + "package": "@databricks/appkit", + "resources": { + "required": [], + "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_NAME", + "description": "Serving endpoint name", + "origin": "user" + } + } + } + ], + "optional": [] + } + } + }, + "scaffolding": { + "command": "databricks apps init", + "flags": { + "--name": { + "description": "Project name — sets {{.projectName}} in package.json, databricks.yml, and .env. Required for non-interactive scaffolding.", + "required": true, + "pattern": "^[a-z][a-z0-9-]*$" + }, + "--template": { + "description": "Template path (local directory or GitHub URL)", + "required": false + }, + "--version": { + "description": "AppKit version to use; defaults to auto-detected", + "required": false + }, + "--features": { + "description": "Plugins to enable (comma-separated, no spaces; must match keys in this manifest's plugins map)", + "required": false, + "pattern": "^[a-zA-Z0-9_-]+(,[a-zA-Z0-9_-]+)*$" + }, + "--set": { + "description": "Set resource values (format: plugin.resourceKey.field=value, repeatable)", + "required": false + }, + "--output-dir": { + "description": "Directory to write the project to", + "required": false + }, + "--description": { + "description": "App description", + "required": false + }, + "--run": { + "description": "Run the app after creation (none, dev, dev-remote)", + "required": false + }, + "--auto-approve": { + "description": "Pass as a bare flag (no value) to skip prompts for optional resources. Not recommended for agent-driven init — conflicts with the 'ask user when in doubt' rule.", + "required": false + }, + "--profile": { + "description": "Databricks CLI profile to use for authentication (global flag)", + "required": false + } + }, + "rules": { + "must": [ + "Treat `databricks apps init` output as starter code, not requirements; adapt or replace it to match the requested app", + "Keep all secrets and credentials only in app.yaml, databricks.yml, and/or .env" + ], + "should": [ + "ask user when in doubt of resource to use for plugin" + ], + "never": [ + "guess resources when multiple or no options are available", + "embed secrets in files that will go to the client-bundle" + ] + } + } +} diff --git a/appkit-agents/client/components.json b/appkit-agents/client/components.json new file mode 100644 index 00000000..13e1db0b --- /dev/null +++ b/appkit-agents/client/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/appkit-agents/client/index.html b/appkit-agents/client/index.html new file mode 100644 index 00000000..f58d98fa --- /dev/null +++ b/appkit-agents/client/index.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + appkit-agents + + +
+ + + diff --git a/appkit-agents/client/postcss.config.js b/appkit-agents/client/postcss.config.js new file mode 100644 index 00000000..51a6e4e6 --- /dev/null +++ b/appkit-agents/client/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + autoprefixer: {}, + }, +}; diff --git a/appkit-agents/client/public/apple-touch-icon.png b/appkit-agents/client/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..32053bd2d17ca6d4cdcfa2e2390cdc4db61658c1 GIT binary patch literal 2547 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 literal 0 HcmV?d00001 diff --git a/appkit-agents/client/public/favicon-16x16.png b/appkit-agents/client/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..d7c16eb471f63a06f45119cebeb085dcc6ce3252 GIT binary patch literal 302 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 Uz+N|&sQ>@~07*qoM6N<$f;65h(*OVf literal 0 HcmV?d00001 diff --git a/appkit-agents/client/public/favicon-512x512.png b/appkit-agents/client/public/favicon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..14d7d209db562940b4e78d23fce203d69afe7c38 GIT binary patch literal 10325 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 boicPbM8)qSin-XseaGC)+VuG$w_E=Y^dlWB literal 0 HcmV?d00001 diff --git a/appkit-agents/client/public/favicon.svg b/appkit-agents/client/public/favicon.svg new file mode 100644 index 00000000..cb30c1e0 --- /dev/null +++ b/appkit-agents/client/public/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/appkit-agents/client/public/site.webmanifest b/appkit-agents/client/public/site.webmanifest new file mode 100644 index 00000000..5693b950 --- /dev/null +++ b/appkit-agents/client/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "appkit-agents", + "short_name": "appkit-agents", + "icons": [ + { + "src": "/favicon-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/favicon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/appkit-agents/client/src/App.tsx b/appkit-agents/client/src/App.tsx new file mode 100644 index 00000000..926a6b74 --- /dev/null +++ b/appkit-agents/client/src/App.tsx @@ -0,0 +1,144 @@ +import { createBrowserRouter, RouterProvider, NavLink, Outlet } from 'react-router'; +import { useState, useEffect } from 'react'; +import { + Button, + Card, + CardContent, + CardHeader, + CardTitle, + Sheet, + SheetContent, + SheetHeader, + SheetTitle, + useIsMobile, +} from '@databricks/appkit-ui/react'; +import { Menu } from 'lucide-react'; +import { AgentChat } from './pages/agents/AgentChat'; + +const navLinkClass = ({ isActive }: { isActive: boolean }) => + `px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${ + isActive + ? 'bg-primary text-primary-foreground' + : 'text-muted-foreground hover:bg-muted hover:text-foreground' + }`; + +const mobileNavLinkClass = ({ isActive }: { isActive: boolean }) => + `block px-3 py-2 rounded-md text-sm font-medium transition-colors ${ + isActive + ? 'bg-primary text-primary-foreground' + : 'text-muted-foreground hover:bg-muted hover:text-foreground' + }`; + +type NavLinkClassFn = (props: { isActive: boolean }) => string; + +function NavLinks({ className, linkClass, onClick }: { className?: string; linkClass: NavLinkClassFn; onClick?: () => void }) { + return ( + + ); +} + +function Layout() { + const isMobile = useIsMobile(); + const [mobileNavOpen, setMobileNavOpen] = useState(false); + + // Close mobile nav when viewport crosses to desktop + useEffect(() => { + if (!isMobile) setMobileNavOpen(false); + }, [isMobile]); + + return ( +
+
+

appkit-agents

+ {/* Desktop nav — hidden below md breakpoint */} + + {/* Mobile nav — visible below md breakpoint */} +
+ + + + + Navigation + + setMobileNavOpen(false)} /> + + +
+
+ +
+ +
+
+ ); +} + +const router = createBrowserRouter([ + { + element: , + children: [ + { path: '/', element: }, + { path: '/agents', element: }, + ], + }, +]); + +export default function App() { + return ; +} + +function HomePage() { + return ( +
+
+

+ Welcome to your Databricks App +

+

+ Powered by Databricks AppKit +

+
+ + + + Getting Started + + +

Your app is ready. Explore the resources below to continue building.

+ +
+
+
+ ); +} diff --git a/appkit-agents/client/src/ErrorBoundary.tsx b/appkit-agents/client/src/ErrorBoundary.tsx new file mode 100644 index 00000000..6a73c26c --- /dev/null +++ b/appkit-agents/client/src/ErrorBoundary.tsx @@ -0,0 +1,75 @@ +import React, { Component } from 'react'; +import type { ReactNode } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@databricks/appkit-ui/react'; + +interface Props { + children: ReactNode; +} + +interface State { + hasError: boolean; + error: Error | null; + errorInfo: React.ErrorInfo | null; +} + +export class ErrorBoundary extends Component { + constructor(props: Props) { + super(props); + this.state = { + hasError: false, + error: null, + errorInfo: null, + }; + } + + static getDerivedStateFromError(error: Error): Partial { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + console.error('ErrorBoundary caught an error:', error); + console.error('Error details:', errorInfo); + this.setState({ + error, + errorInfo, + }); + } + + render() { + if (this.state.hasError) { + return ( +
+ + + Application Error + + +
+
+

Error Message:

+
{this.state.error?.toString()}
+
+ {this.state.errorInfo && ( +
+

Component Stack:

+
+                      {this.state.errorInfo.componentStack}
+                    
+
+ )} + {this.state.error?.stack && ( +
+

Stack Trace:

+
{this.state.error.stack}
+
+ )} +
+
+
+
+ ); + } + + return this.props.children; + } +} diff --git a/appkit-agents/client/src/index.css b/appkit-agents/client/src/index.css new file mode 100644 index 00000000..0ce57a7d --- /dev/null +++ b/appkit-agents/client/src/index.css @@ -0,0 +1,82 @@ +@import "@databricks/appkit-ui/styles.css"; + +:root { + /* --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.21 0.006 285.885); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.985 0 0); + --success: oklch(0.603 0.135 166.892); + --success-foreground: oklch(1 0 0); + --warning: oklch(0.795 0.157 78.748); + --warning-foreground: oklch(0.199 0.027 238.732); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.705 0.015 286.067); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.21 0.006 285.885); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.705 0.015 286.067); */ +} + +@media (prefers-color-scheme: dark) { + :root { + /* --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.92 0.004 286.32); + --primary-foreground: oklch(0.21 0.006 285.885); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --destructive-foreground: oklch(0.985 0 0); + --success: oklch(0.67 0.12 167); + --success-foreground: oklch(1 0 0); + --warning: oklch(0.83 0.165 85); + --warning-foreground: oklch(0.199 0.027 238.732); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.552 0.016 285.938); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.552 0.016 285.938); */ + } +} diff --git a/appkit-agents/client/src/lib/utils.ts b/appkit-agents/client/src/lib/utils.ts new file mode 100644 index 00000000..2819a830 --- /dev/null +++ b/appkit-agents/client/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/appkit-agents/client/src/main.tsx b/appkit-agents/client/src/main.tsx new file mode 100644 index 00000000..d8cb1032 --- /dev/null +++ b/appkit-agents/client/src/main.tsx @@ -0,0 +1,27 @@ +import { + ResourceStatusIndicator, + ResourceStatusProvider, +} from '@databricks/appkit-ui/react'; +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App.tsx'; +import { ErrorBoundary } from './ErrorBoundary.tsx'; + +createRoot(document.getElementById('root')!).render( + + + {/* + * Surfaces resource readiness (e.g. SQL warehouse cold-starts) as a + * single sonner toast across the whole tree. Both are no-ops when + * nothing's pending; remove them to render the aggregate yourself + * via useResourceStatus(). Apps that already mount their own + * can swap the indicator for useResourceStatusToaster(). + */} + + + + + + +); diff --git a/appkit-agents/client/src/pages/agents/AgentChat.tsx b/appkit-agents/client/src/pages/agents/AgentChat.tsx new file mode 100644 index 00000000..de87c9db --- /dev/null +++ b/appkit-agents/client/src/pages/agents/AgentChat.tsx @@ -0,0 +1,232 @@ +import { useEffect, useRef, useState } from 'react'; +import { + type AgentChatEvent, + Button, + Card, + CardContent, + Input, + useAgentChat, + usePluginClientConfig, +} from '@databricks/appkit-ui/react'; + +interface Message { + id: string; + role: 'user' | 'assistant' | 'tool'; + content: string; + toolName?: string; +} + +/** + * Shape of the agents plugin's `clientConfig()` payload — exposed by + * the agents plugin at server startup and inlined into the boot HTML + * via `