feat: add Vector Search plugin#200
Conversation
Brainstorm: add PR #166 (Agent plugin) and PR #200 (Vector Search plugin) as future extension references. Rename future enhancement section to cover both Vector Search and Lakebase pgvector options. Plan: address findings from multi-agent code review (architecture, security, performance, spec flow, pattern recognition): - Fix cache infrastructure: use shared CacheManager pool, not fictional maxEntries config - Clarify error contract: programmatic API errors propagate, HTTP handlers use execute() for interceptors - Separate _chatCollect()/_embed() from HTTP handlers - Add SSE buffer max size (1MB) to prevent OOM - Restrict response_format to text/json_object (no json_schema v1) - Add runtime role validation against known set - Add model to parameter allowlist for Foundation Model API - Add stop parameter bounds (4 entries, 256 chars) - Standardize connection pool at 100 (was contradictory 50/100) - Add retry on 503 for chatCollect() (cold-start resilience) - Specify setup() throws on missing endpoint, shutdown() cleanup - Extract SSE parser to stream/sse-parser.ts in Phase 2 - Add per-route body-parser middleware (not global) - Update acceptance criteria and security checklist Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com>
Brainstorm: - Added chatCollect() for non-streaming programmatic API - Scoped out vision/multimodal, thinking/budget_tokens, tools/tool_choice as v2 items with specific rationale - Added reasoning_effort to v1 scope - Referenced PRs #166 (agent plugin) and #200 (vector search) - Updated references with query/vision/reasoning/function-calling docs Plan: - Cross-referenced Databricks Query API spec vs OpenAI conventions - Documented type sourcing decision (hand-write for v1, sourced from OpenAI API reference) - Added SDK comparison table (OpenAI vs Anthropic vs AppKit) - Fixed id: string | null in response types - Noted served-model-name header for telemetry - Documented extra_params vs top-level field convention Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com>
Brainstorm: add PR #166 (Agent plugin) and PR #200 (Vector Search plugin) as future extension references. Rename future enhancement section to cover both Vector Search and Lakebase pgvector options. Plan: address findings from multi-agent code review (architecture, security, performance, spec flow, pattern recognition): - Fix cache infrastructure: use shared CacheManager pool, not fictional maxEntries config - Clarify error contract: programmatic API errors propagate, HTTP handlers use execute() for interceptors - Separate _chatCollect()/_embed() from HTTP handlers - Add SSE buffer max size (1MB) to prevent OOM - Restrict response_format to text/json_object (no json_schema v1) - Add runtime role validation against known set - Add model to parameter allowlist for Foundation Model API - Add stop parameter bounds (4 entries, 256 chars) - Standardize connection pool at 100 (was contradictory 50/100) - Add retry on 503 for chatCollect() (cold-start resilience) - Specify setup() throws on missing endpoint, shutdown() cleanup - Extract SSE parser to stream/sse-parser.ts in Phase 2 - Add per-route body-parser middleware (not global) - Update acceptance criteria and security checklist Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com>
Brainstorm: - Added chatCollect() for non-streaming programmatic API - Scoped out vision/multimodal, thinking/budget_tokens, tools/tool_choice as v2 items with specific rationale - Added reasoning_effort to v1 scope - Referenced PRs #166 (agent plugin) and #200 (vector search) - Updated references with query/vision/reasoning/function-calling docs Plan: - Cross-referenced Databricks Query API spec vs OpenAI conventions - Documented type sourcing decision (hand-write for v1, sourced from OpenAI API reference) - Added SDK comparison table (OpenAI vs Anthropic vs AppKit) - Fixed id: string | null in response types - Noted served-model-name header for telemetry - Documented extra_params vs top-level field convention Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com>
There was a problem hiding this comment.
These comments from the AI review make sense to me:
#: 1
Severity: critical
File: plugins/vector-search/vector-search.ts
Line: 175-189
Issue: Missing execute() interceptor
Description: Every other plugin wraps API calls in this.execute() for cache/retry/timeout/telemetry. This plugin calls
the
connector directly, bypassing the entire interceptor chain.
────────────────────────────────────────
#: 2
Severity: high
File: plugins/vector-search/defaults.ts
Line: 1-7
Issue: Defaults defined but never used
Description: vectorSearchDefaults is exported but never imported anywhere — dead code.
────────────────────────────────────────
#: 3
Severity: high
File: connectors/vector-search/client.ts
Line: 24-26
Issue: timeout config stored but never applied
Description: Constructor stores this.config.timeout but no AbortSignal or timeout mechanism is ever created from it.
────────────────────────────────────────
#: 4
Severity: high
File: plugins/vector-search/vector-search.ts
Line: 140
Issue: No request body validation
Description: req.body is cast directly to SearchRequest without Zod validation. The repo design philosophy states
"Type-safe — Heavy TypeScript usage with runtime validation (Zod)". Other plugins validate at system boundaries.
────────────────────────────────────────
#: 5
Severity: medium
File: plugins/vector-search/vector-search.ts
Line: 349
Issue: fromCache: false hardcoded
Description: Since execute() isn't used, caching never happens — this field is always false, making it misleading in the
response type.
────────────────────────────────────────
#: 6
Severity: medium
File: plugins/vector-search/vector-search.ts
Line: 296-298
Issue: shutdown() calls streamManager.abortAll() but plugin never uses streaming
Description: The plugin has no SSE streams. This won't error (base class provides streamManager), but it's misleading
about the plugin's capabilities.
────────────────────────────────────────
#: 7
Severity: medium
File: plugins/vector-search/vector-search.ts
Line: 234
Issue: Non-null assertion on endpointName!
Description: indexConfig.endpointName! uses ! operator. Validated at setup time, but if config changes dynamically or
setup is skipped, this will throw an untyped error.
atilafassina
left a comment
There was a problem hiding this comment.
Great work!
I left a few comments, some are minor but I think there are 3 critical ones
- not wrapping the plugin connector calls in the executor
- returning plain objects as error
- not wrapping the
embeddingFnin a try/catch
The AbortControl in the connector would be nice to have too, but that's not a big issue if we have the plugin calls wrapped in the executor.
The "hybrid" hardcode in the response is mostly a question from me, maybe it's a non-issue.
Once we land in the right API, we need to add docs and a route in the dev-playground. Btu I'd defer that for after a the reviews to avoid re-work. Ofc, you do what you prefer :)
pkosiec
left a comment
There was a problem hiding this comment.
Thanks a lot for your contribution! I think most have been already said by Atila and Jorge (e.g. about the dev playground addition), just a few small comments from my side.
IMO the most important point is to figure out the apps init flow (configure required resource(s) for the plugin, to be selectable interactively as a part of the init flow), and also make sure that the API is designed around the resource. See the Files, Genie or Lakebase plugins as an example. Thanks!
Adds @databricks/appkit-vector-search — a plugin that gives Databricks Apps built with AppKit instant vector search query capabilities. Ships backend (Express routes, VS REST API client, auth) and frontend (React hook, styled components with Radix UI). Developer experience target: ~45 lines for a full search page with search box, results, filters, and keyword highlighting. 82 tests included. Validated against real VS index on dogfood.
- Move from packages/vector-search/ into packages/appkit/src/plugins/vector-search/ - Replace custom auth (ServicePrincipalTokenProvider, OboTokenExtractor) with AppKit's built-in asUser(req) and getWorkspaceClient() context - Add VectorSearchConnector using workspaceClient.apiClient.request() instead of raw fetch with manual token management - Plugin now extends Plugin base class with proper manifest.json, defaults.ts, this.route(), this.execute(), and toPlugin() factory - Remove standalone package.json, tsconfig.json, and vitest config - Register plugin and connector in index barrel exports Addresses review feedback: - Plugin lives under plugins/ folder alongside analytics, genie, files - No custom auth handling — uses AppKit's built-in mechanisms - Follows create-core-plugin patterns (manifest, defaults, connector) Signed-off-by: Adam Gurary <adam.gurary@databricks.com>
- Connector: wrap VS API calls in telemetry spans with index name, query type, result count, and latency attributes - Connector: check AbortSignal before executing requests - Connector: add WideEvent context logging with query metadata - Plugin: replace this.execute() in route handlers with direct try/catch — preserves actual error details (code, message, status) instead of swallowing them into undefined - Remove unused SearchFilters import Signed-off-by: Adam Gurary <adam.gurary@databricks.com>
- Wrap all connector calls in this.execute() for retry/cache/timeout/telemetry
- Use vectorSearchDefaults (was dead code, now feeds execute())
- Pass AbortSignal from execute() to connector methods
- Change error responses to {error, plugin} shape matching Files/Genie
- Throw Error instances instead of plain objects in programmatic API
- Wrap user-provided embeddingFn in try/catch
- Extract shared query prep logic into _prepareQuery() helper
- Remove fromCache (execute handles caching transparently)
- Remove streamManager.abortAll() from shutdown (no streams)
- Replace endpointName! assertion with runtime guard
- Use index queryType for next-page response instead of hardcoded "hybrid"
- Add vector_search_index resource to manifest for apps init flow
- Add dev-playground integration (server, route, nav)
- Add plugin documentation page
- Add template integration (App.tsx, VectorSearchPage, plugins.json)
- Export vectorSearch from top-level package index
- Update tests for new patterns (execute, error shapes, embeddingFn)
Co-authored-by: Isaac
Signed-off-by: Adam Gurary <adam.gurary@databricks.com>
d6bd39a to
2f89bdf
Compare
|
Addressed all review feedback from @atilafassina, @calvarjorge, and @pkosiec. Rebased onto current main. Blockers (Atila)
Pattern alignment
New content
Skipped
Note on CICI doesn't appear to run on fork PRs (protected runner group). Could not run lint/typecheck/tests locally either — the Databricks npm proxy ( |
atilafassina
left a comment
There was a problem hiding this comment.
🏆 Amazing work, @adamgurary
thank you very much
What's next:
I'll QA and update the docs, then we'll prepare a new release with the plugin available.
I'll ping you for review when writing the skills for it
1b20b49 to
0081ab1
Compare
Co-authored-by: Isaac Signed-off-by: Atila Fassina <atila@fassina.eu>
0081ab1 to
1ec2e11
Compare
Summary
@databricks/appkit-vector-search— a plugin that gives Databricks Apps built with AppKit instant vector search query capabilitiesWhat's included
Backend plugin (
src/plugin/):VectorSearchPlugin.ts— plugin class with lifecycle, config, route injectionVectorSearchClient.ts— REST API client for VS endpointsroutes.ts— Express route handlersauth.ts— service principal default, OBO opt-in per indexFrontend UI (
src/ui/):useVectorSearchReact hookSearchBox,SearchResults,SearchResultCard,SearchLoadMorecomponentsDesign decisions
Test plan
vitest runinpackages/vector-search/)