feat(appkit): ship the ai-search plugin at beta - #508
Open
MarioCadenas wants to merge 15 commits into
Open
Conversation
MarioCadenas
marked this pull request as draft
August 3, 2026 09:17
MarioCadenas
force-pushed
the
feat/export-vector-search-plugin
branch
from
August 3, 2026 16:10
2fd6e3f to
380e668
Compare
Contributor
📦 Bundle size reportCompared against
|
| dist | raw | gzip |
|---|---|---|
| JS (runtime) | 839 KB (+15 KB) | 293 KB (+5.1 KB) |
| Type declarations | 311 KB (+4.6 KB) | 107 KB (+1.9 KB) |
| Source maps | 1.6 MB (+22 KB) | 547 KB (+7.4 KB) |
| Other | 11 KB | 3.7 KB |
| Total | 2.8 MB (+42 KB) | 951 KB (+14 KB) |
Per-entry composition (own code — deps external (as shipped))
| Entry | Initial (gz) | Lazy (gz) | Total (gz) | node_modules (min) | Own code (min) |
|---|---|---|---|---|---|
. |
86 KB (+20 B) | 2.5 KB | 89 KB (+20 B) | external | 282 KB (+94 B) |
./beta |
47 KB (+2.3 KB) | 459 B (+1 B) | 47 KB (+2.3 KB) | external | 138 KB (+8.3 KB) |
./type-generator |
19 KB | 0 B | 19 KB | external | 54 KB |
Chunks:
| Entry | Chunk | Load | Size (gz) |
|---|---|---|---|
. |
index.js |
initial | 82 KB |
. |
utils.js |
initial | 4.0 KB |
. |
remote-tunnel-manager.js |
lazy | 2.5 KB |
./beta |
beta.js |
initial | 31 KB |
./beta |
stream-manager.js |
initial | 5.8 KB |
./beta |
wide-event-emitter.js |
initial | 3.2 KB |
./beta |
databricks.js |
initial | 3.0 KB |
./beta |
configuration.js |
initial | 2.1 KB |
./beta |
service-context.js |
initial | 1.3 KB |
./beta |
client.js |
initial | 431 B |
./beta |
client-options.js |
initial | 220 B |
./beta |
supervisor-api.js |
lazy | 194 B |
./beta |
databricks.js |
lazy | 142 B |
./beta |
index.js |
lazy | 123 B |
./type-generator |
index.js |
initial | 19 KB |
@databricks/appkit-ui
npm tarball (packed): 305 KB — gzipped download (dist + bin; excludes release-only docs/NOTICE).
| dist | raw | gzip |
|---|---|---|
| JS (runtime) | 360 KB | 119 KB |
| Type declarations | 205 KB | 74 KB |
| Source maps | 686 KB | 224 KB |
| CSS | 16 KB | 3.3 KB |
| Total | 1.2 MB | 422 KB |
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
| Entry | Initial (gz) | Lazy (gz) | Total (gz) | node_modules (min) | Own code (min) |
|---|---|---|---|---|---|
./js |
4.3 KB | 49 KB | 54 KB | 208 KB | 12 KB |
./js/beta |
20 B | 0 B | 20 B | 0 B | 0 B |
./react |
429 KB | 49 KB | 478 KB | 1.3 MB | 168 KB |
./react/beta |
20 B | 0 B | 20 B | 0 B | 0 B |
Chunks:
| Entry | Chunk | Load | Size (gz) |
|---|---|---|---|
./js |
index.js |
initial | 4.2 KB |
./js |
chunk |
initial | 120 B |
./js |
apache-arrow |
lazy | 49 KB |
./js/beta |
beta.js |
initial | 20 B |
./react |
index.js |
initial | 427 KB |
./react |
tslib |
initial | 2.1 KB |
./react |
apache-arrow |
lazy | 49 KB |
./react/beta |
beta.js |
initial | 20 B |
Contributor
🤖 AppKit PR bot🔬 Run evalsStart an eval for this PR from the evals-monitor app: Go to Evals Monitor → 📦 Try this PR's app templateScaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh run download 30920811625 -R databricks/appkit -n appkit-template-0.52.0-pr.64fe161-feat-export-vector-search-plugin-508 -D appkit-pr-508 \
&& unzip -o "appkit-pr-508/appkit-template-0.52.0-pr.64fe161-feat-export-vector-search-plugin-508.zip" -d "appkit-pr-508" \
&& databricks apps init --template "appkit-pr-508"The template pins |
MarioCadenas
marked this pull request as ready for review
August 3, 2026 16:21
The vector-search plugin was fully implemented but unexported: its manifest had `hidden: true`, and the auto-generated export barrels are driven by each manifest's `stability` field. Flip the manifest to `stability: beta` and teach the barrel generator to derive the camelCase binding from a kebab-case manifest name (`vector-search` -> `vectorSearch`), mirroring `manifestNameToBinding` in the plugin `promote` command. The folder-name check is relaxed to the schema charset since it is only interpolated into a string path. `vectorSearch` now ships from `@databricks/appkit/beta`, alongside its public config and query types. The dev-playground consumer is re-enabled (clearing its standing TODO). The runtime route stays `/api/vector-search`, so docs and existing clients are unaffected. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Rename the beta plugin's public identity from `vectorSearch` to `aiSearch` end to end: manifest name/displayName, folder and file names, the plugin and connector classes, config/binding symbols, telemetry scopes, docs, and the dev-playground + template consumers. The runtime route follows `manifest.name`, so it moves from `/api/vector-search` to `/api/ai-search`. Databricks-platform identifiers are intentionally left as-is since they name the underlying product, not this plugin: the `vector_search_index` resource type and its schema/permissions, the `/api/2.0/vector-search` REST paths, `Vs*` wire-format types, `vs.*` span attributes, `DATABRICKS_VS_*` env vars, and the "Vector Search Index" resource alias. Regenerated the beta export barrel, template appkit.plugins.json, the dev-playground route tree, and the typedoc API reference. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
The ai-search plugin ships from the /beta subpath, but the doc examples imported it from the main entry. Split the imports so `aiSearch` comes from `@databricks/appkit/beta` while `createApp`/`server` stay on the main entry, matching the agents plugin docs. Also add the import header to the programmatic-access example so it is copy-pasteable. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
The 'Check generated types are up to date' step diffs a hardcoded list of plugin doc pages. After renaming the plugin, the doc page moved from vector-search.md to ai-search.md, but the workflow still referenced the old path — so `git diff` exited 128 (unknown path) and failed the check with a misleading 'out of sync' message rather than a real drift. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
…dling
Addresses issues found in review of the ai-search plugin:
- embeddingFn no longer clears queryText for hybrid/full_text queries. It
was cleared unconditionally, which silently degraded hybrid to vector-only
and produced invalid full_text requests. Now only ann drops the text, and
full_text skips embedding entirely.
- Forward the execution's AbortSignal to the Vector Search REST call via the
SDK Context/CancellationToken (mirroring the serving connector), so the
configured timeout and client-disconnect actually cancel an in-flight
query. Both query and queryNextPage were affected.
- _handleError now gates the raw error message on NODE_ENV, matching the
base Plugin.execute() convention, so upstream error text (e.g. from a
user-supplied embeddingFn) isn't leaked to clients in production.
- Fix the /query response example in the docs to the real nested shape
({ results: [{ score, data }], totalCount, queryTimeMs, queryType,
nextPageToken }); it previously showed a flat row shape.
- Add tests for the three route handlers and their validation branches,
queryNextPage wiring, _parseResponse edge cases (no score column, present
next_page_token, latency_ms fallback), request-level overrides, object and
request-suppressed reranker, and the embedding query-type matrix. Plugin
line coverage 49% -> 91%.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
SearchResponse<T>/SearchResult<T> were parameterized over the row shape, but query() returned the non-generic default and took no type parameter, so consumers could never supply T — result.data was always Record<string, unknown> and had to be cast. Thread the generic through query<T>() and _parseResponse<T>() so `appkit.aiSearch.query<MyRow>(...)` yields a typed result.data. The dynamic column-to-data mapping keeps one boundary cast (data as T); the caller asserts T matches the configured columns. Backward-compatible: T defaults to Record<string, unknown>, so existing callers are unaffected. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Multi-word plugins were only reachable on the appkit handle by their kebab-case manifest name (e.g. appkit["ai-search"]), because the accessor key, the TS PluginMap type, and the HTTP route prefix all derive from manifest.name. Add a camelCase alias derived from that name so ai-search is usable as appkit.aiSearch. - New kebabToCamel() runtime helper + KebabToCamel<S> type in shared. - appkit.ts registers the plugin under its kebab name and, when the camelCase form differs, additively defines the same accessor under the alias. The kebab key keeps working; the HTTP route stays /api/ai-search. - PluginMap gains the camelCase keys as an intersection, so both appkit.aiSearch and appkit["ai-search"] type-check. Single-word names are a no-op (camel === kebab), so no behavior change there. This is uniform, so the GA ui-variants plugin also gains a uiVariants alias alongside its existing ui-variants key — additive, nothing breaks. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
…b alias The previous change exposed multi-word plugins under both keys (appkit.aiSearch and appkit["ai-search"]). Register the accessor under the camelCase key only, so there is a single canonical handle. Internal lookups (#pluginInstances, context.registerPlugin) and the HTTP route prefix still use the kebab name — only the public property changes. PluginMap maps solely to the camelCase keys to match, so appkit["ai-search"] is now a type error as well as undefined at runtime. Single-word plugins are unaffected (camel === kebab). The GA ui-variants plugin moves from appkit["ui-variants"] to appkit.uiVariants (inert handle, no exports; route /api/ui-variants unchanged); its tests are updated accordingly. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
…bab-camel
Cleanups from a /simplify pass over the ai-search branch:
- _prepareQuery now returns Omit<VsQueryParams,"indexName"> (carries filters,
renames rerankerConfig->reranker), so the /query route and programmatic
query() collapse to `{ indexName, ...prepared }` instead of duplicating an
8-field payload. Removes the accidental body.filters vs request.filters
split.
- Hoist the AbortSignal->CancellationToken bridge into a shared
connectors/context.ts (cancellationTokenFromAbortSignal +
contextFromAbortSignal); ai-search and serving import it instead of each
carrying a copy.
- promote.ts uses the shared kebabToCamel instead of its own
manifestNameToBinding copy.
- _parseResponse skips the score column by index (i !== scoreIndex) rather
than a per-cell string compare.
- Trim verbose comments introduced on the branch to the one-line bar.
Left alone (flagged, out of scope): the jobs connector's separate
_createContext variant (different listener semantics), scaffold.ts's
toCamelCase (different impl), and tools/generate-plugin-entries.ts (package
boundary).
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
setup() re-validated fields the TS types and manifest config.schema already require (at least one index; columns non-empty), duplicating guarantees the config layer provides. Drop those and keep only the two runtime checks types can't express: an empty indexName (an env var populating it was unset) and the pagination -> endpointName dependency. Also drops the stale 'Vector Search plugin configured' debug log (no sibling plugin logs this). This does not overlap with the framework's ResourceRegistry, which validates the manifest's declared resource env vars, not the indexes config object. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Make IndexConfig.indexName optional and resolve it from the DATABRICKS_VS_INDEX_NAME env var when omitted, so the manifest's declared resource env var actually feeds config. columns stays required (the VS query API requires it and it can't be env-derived). Resolution happens once in _resolveIndex, so both the HTTP routes and the programmatic query() pick up the default; setup() now rejects only an index whose indexName resolves from neither config nor env. Manifest config.schema drops indexName from required. Also document that IndexConfig.auth governs the built-in HTTP routes; programmatic callers select OBO per call via appkit.aiSearch.asUser(req). auth is kept as the declarative route toggle, mirroring the files plugin (the only other plugin with per-resource auth config). Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
setup() threw unconditionally on an index whose indexName resolves from neither config nor DATABRICKS_VS_INDEX_NAME. That contradicted the framework's resource-validation policy, which only warns for missing resources in dev (NODE_ENV=development) and throws in prod. It also fired after that warning had already been emitted, so a dev app crashed despite the graceful path. Mirror the framework policy: warn and skip the unusable index in dev, throw outside dev. The pagination -> endpointName check stays a hard throw in all modes since it's a config logic error, not a missing resource. The dev-playground demo drops its manual env fallback now that the plugin resolves indexName from the env var itself. The genie plugin has the same unconditional-throw pattern for a missing space ID; left as-is (out of scope) but worth a follow-up. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
…licy setup()'s indexName-presence check duplicated the framework's resource validation, which already warns-in-dev / throws-in-prod for a missing DATABRICKS_VS_INDEX_NAME and runs before setup(). The inline NODE_ENV gate was therefore redundant, its prod-throw branch was unreachable in the real createApp flow, and it silently ignored APPKIT_STRICT_VALIDATION (which the registry honors). Drop it and keep only the pagination -> endpointName check, a config logic error the registry can't see. Unresolved aliases still 404 on the routes and throw in query(). Removes the two now-obsolete setup() tests. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
MarioCadenas
force-pushed
the
feat/export-vector-search-plugin
branch
from
August 4, 2026 13:14
eda8ff2 to
60ebe31
Compare
… can reuse it The earlier dedup pointed promote.ts at kebabToCamel in plugin.ts. That resolved in-repo but broke the published tarball: the CLI is copied to appkit's dist/cli flattened, and its `../../../../plugin` import pointed at a dist/plugin.js that dist-appkit.ts never copies — so `npm run typegen` in the template failed with ERR_MODULE_NOT_FOUND (caught by the PR Template Artifact check). Move kebabToCamel + KebabToCamel into a dependency-free leaf module shared/src/naming.ts. plugin.ts re-exports them (so `from "shared"` consumers like appkit.ts are unchanged), promote.ts imports from ../../../../naming, and dist-appkit.ts copies naming.js next to the CLI in the tarball — mirroring how it already copies schemas/. Verified by building the prerelease tarball and loading the bundled promote.js: it now resolves naming.js and imports cleanly. Genuine single-source (no duplicated helper) that survives bundling, rather than dragging the heavy plugin.ts graph into the CLI bundle. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
…outes
Scaffolding an app with a multi-word plugin generated invalid server code
(`import { ai-search }` — a hyphen isn't a valid JS identifier) and the client
view didn't render (the template guards `.plugins.aiSearch` but the plugin-map
key was `ai-search`). Go templates can't express `.plugins.ai-search` at all.
ai-search is the first multi-word plugin, so it exposed that the whole system
assumes manifest.name is a valid JS identifier.
Make manifest.name camelCase (aiSearch, uiVariants) the single canonical
identifier — SDK accessor, JS export binding, and template plugin-map key.
Derive the kebab HTTP route (/api/ai-search) and doc filename via a new
camelToKebab; folders stay kebab. This inverts the earlier kebabToCamel
accessor approach: the accessor and PluginMap are now identity.
- naming.ts: add camelToKebab; plugin.ts re-exports it, PluginMap keys on
P["name"] directly.
- server/index.ts + plugin.ts: route prefix uses camelToKebab(name).
- appkit.ts: accessor key is name verbatim.
- schema name regex loosened to ^[a-z][a-zA-Z0-9-]*$ at the two plugin-name
sites only (resourceKey + project-name patterns untouched); create.ts and
the two generators updated; generate-plugin-entries splits name (camel) vs
folder (kebab) patterns; doc-banners derives the kebab .md basename.
- Rename ai-search->aiSearch and ui-variants->uiVariants manifests + type
params + name-assertion test; regenerated schemas + template.
Verified end to end: scaffolding with --features=aiSearch now emits valid
`import { aiSearch }`, wires the AiSearchPage view, and keeps /api/ai-search
and /api/ui-variants/confirm routes. Full suite + typecheck + knip + tarball
load all pass.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Exports the vector-search plugin at beta, renames it to ai-search, and makes it cleanly consumable from
@databricks/appkit/beta.The plugin was fully implemented but unexported (
"hidden": true, and the export barrels are auto-generated from each manifest'sstabilityfield). This PR ships it, renames it toai-searchto match its product positioning, and addresses a set of correctness/API issues found while reviewing it.Changes
Export + rename
stability: beta; the barrel generator derives the camelCase binding from the kebab manifest name (ai-search→aiSearch).vector-search→ai-searchend to end: plugin + connector dirs/classes, config/binding symbols, telemetry scopes, docs, dev-playground + template consumers, and the HTTP route (/api/ai-search). Databricks-product identifiers stay as-is (thevector_search_indexresource type,/api/2.0/vector-searchREST paths,Vs*wire types,DATABRICKS_VS_*env vars).API / consumability
appkit.aiSearchis the canonical SDK accessor: a sharedkebabToCamelmaps multi-word plugin names to camelCase handle keys (the kebab key is no longer exposed). The route prefix stays kebab.query<T>()is generic over the result row type, soresult.datais typed.IndexConfig.indexNameis optional and defaults toDATABRICKS_VS_INDEX_NAME;columnsstays required (the VS query API requires it).authdocumented as the built-in-route OBO toggle; programmatic callers useasUser(req).Correctness / security (from review)
embeddingFnno longer clearsqueryTextfor hybrid/full_text (was silently degrading hybrid to vector-only and breaking full_text).AbortSignalto the VS REST call via the SDKContext, so the configured timeout actually cancels in-flight queries. The abort→cancellation bridge is hoisted to a sharedconnectors/context.ts(also adopted by the serving connector)._handleErrorgates the raw error message onNODE_ENV, matchingPlugin.execute().setup()no longer re-implements missing-resource policy — the framework's resource validation owns warn-in-dev / throw-in-prod;setup()keeps only the pagination→endpointName config check./queryresponse example to the real{ results: [{ score, data }], totalCount, queryTimeMs, queryType, nextPageToken }shape.Cleanup
/queryroute vs programmaticquery()payload; single-sourcedkebabToCamel(droppedpromote.ts's copy).Consumers:
import { aiSearch } from "@databricks/appkit/beta"→appkit.aiSearch.query(...)→ route/api/ai-search.Verification
pnpm -r typecheck— all packages passpnpm buildclean, no generated-file drift;pnpm checkandknippassmain(v0.52.0), integrating the workspace-client wrapper (feat(appkit): introduce workspace-client wrapper over the Databricks SDK #475)