@dbx-tools/shared-search 0.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/index.ts +7 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +6 -0
- package/lib/src/search.d.ts +171 -0
- package/lib/src/search.js +189 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +54 -0
- package/src/search.ts +259 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @dbx-tools/shared-search
|
|
2
|
+
|
|
3
|
+
Browser-safe AI Search (Databricks Vector Search) schemas and inferred types.
|
|
4
|
+
|
|
5
|
+
Import this package when a UI, Mastra tool schema, server route, or test needs
|
|
6
|
+
to validate the same search payloads that
|
|
7
|
+
[`@dbx-tools/search`](../../node/search) reads and writes.
|
|
8
|
+
|
|
9
|
+
Key features:
|
|
10
|
+
|
|
11
|
+
- Shared `SearchRequest` / `SearchResult` contract with `{ id, score, fields }`
|
|
12
|
+
hits, so an autocomplete box, a docs lookup, and a Mastra tool speak one shape.
|
|
13
|
+
- `SearchMode` (`hybrid` / `vector` / `keyword`) matching the serving query
|
|
14
|
+
types, with hybrid as the sensible default.
|
|
15
|
+
- `UniversalSearchRequest` for federated search across several indexes at once.
|
|
16
|
+
- `SearchDocument` / `UpsertResult` for adding and updating direct-access index
|
|
17
|
+
contents.
|
|
18
|
+
- `SearchIndexInfo` / `SearchClientConfig` for the boot-time index catalogue a
|
|
19
|
+
search box reads with no server round-trip.
|
|
20
|
+
- Model/tool-friendly schemas that avoid JSON Schema constraints known to cause
|
|
21
|
+
problems with some serving endpoints.
|
|
22
|
+
|
|
23
|
+
## Validate A Search Request
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { search, type SearchRequest } from "@dbx-tools/shared-search";
|
|
27
|
+
|
|
28
|
+
const request: SearchRequest = search.searchRequestSchema.parse({
|
|
29
|
+
query: "reset my password",
|
|
30
|
+
index: "main.support.docs",
|
|
31
|
+
limit: 5,
|
|
32
|
+
mode: "hybrid",
|
|
33
|
+
filter: { locale: "en", published: true },
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Validate Search Results
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const result = search.searchResultSchema.parse(await response.json());
|
|
41
|
+
for (const hit of result.hits) {
|
|
42
|
+
console.log(hit.score, hit.id, hit.fields.title);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`searchHitSchema` returns the requested columns under `fields`, keyed by column
|
|
47
|
+
name, plus the primary-key `id` and a relevance `score`. A federated hit also
|
|
48
|
+
carries the `index` it came from.
|
|
49
|
+
|
|
50
|
+
## Read The Index Catalogue
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const config = search.searchClientConfigSchema.parse(
|
|
54
|
+
await fetch("/api/search/indexes").then((r) => r.json()),
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`searchClientConfigSchema` is what a search box reads at boot to know which
|
|
59
|
+
indexes it may query, which is the default, and the page size to request.
|
|
60
|
+
|
|
61
|
+
## Module
|
|
62
|
+
|
|
63
|
+
- `search` - `searchModeSchema`, `searchRequestSchema`, `searchHitSchema`,
|
|
64
|
+
`searchResultSchema`, `universalSearchRequestSchema`, `searchDocumentSchema`,
|
|
65
|
+
`upsertResultSchema`, `searchIndexInfoSchema`, `searchClientConfigSchema`, and
|
|
66
|
+
the flat inferred types (`SearchMode`, `SearchRequest`, `SearchHit`,
|
|
67
|
+
`SearchResult`, `UniversalSearchRequest`, `SearchDocument`, `UpsertResult`,
|
|
68
|
+
`SearchIndexInfo`, `SearchClientConfig`).
|
|
69
|
+
|
|
70
|
+
Runtime search, index management, the Mastra tools, and the AppKit plugin live
|
|
71
|
+
in [`@dbx-tools/search`](../../node/search). The React search box lives in
|
|
72
|
+
[`@dbx-tools/ui-search`](../../ui/search).
|
package/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// GENERATED by projen watch - DO NOT EDIT.
|
|
2
|
+
// Regenerated from the exporting modules in ./src.
|
|
3
|
+
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
|
+
|
|
5
|
+
export * as search from "./src/search.ts";
|
|
6
|
+
export { searchModeSchema, searchRequestSchema, searchHitSchema, searchResultSchema, universalSearchRequestSchema, searchDocumentSchema, upsertResultSchema, searchIndexInfoSchema, searchClientConfigSchema, createIndexRequestSchema, syncIndexRequestSchema, indexInfoSchema } from "./src/search.ts";
|
|
7
|
+
export type { SearchMode, SearchRequest, SearchHit, SearchResult, UniversalSearchRequest, SearchDocument, UpsertResult, SearchIndexInfo, SearchClientConfig, CreateIndexRequest, SyncIndexRequest, IndexInfo } from "./src/search.ts";
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export * as search from "./src/search.ts";
|
|
2
|
+
export { searchModeSchema, searchRequestSchema, searchHitSchema, searchResultSchema, universalSearchRequestSchema, searchDocumentSchema, upsertResultSchema, searchIndexInfoSchema, searchClientConfigSchema, createIndexRequestSchema, syncIndexRequestSchema, indexInfoSchema } from "./src/search.ts";
|
|
3
|
+
export type { SearchMode, SearchRequest, SearchHit, SearchResult, UniversalSearchRequest, SearchDocument, UpsertResult, SearchIndexInfo, SearchClientConfig, CreateIndexRequest, SyncIndexRequest, IndexInfo } from "./src/search.ts";
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// GENERATED by projen watch - DO NOT EDIT.
|
|
2
|
+
// Regenerated from the exporting modules in ./src.
|
|
3
|
+
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
|
+
export * as search from "./src/search.js";
|
|
5
|
+
export { searchModeSchema, searchRequestSchema, searchHitSchema, searchResultSchema, universalSearchRequestSchema, searchDocumentSchema, upsertResultSchema, searchIndexInfoSchema, searchClientConfigSchema, createIndexRequestSchema, syncIndexRequestSchema, indexInfoSchema } from "./src/search.js";
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssTUFBTSxNQUFNLGlCQUFpQixDQUFDO0FBQzFDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxtQkFBbUIsRUFBRSxlQUFlLEVBQUUsa0JBQWtCLEVBQUUsNEJBQTRCLEVBQUUsb0JBQW9CLEVBQUUsa0JBQWtCLEVBQUUscUJBQXFCLEVBQUUsd0JBQXdCLEVBQUUsd0JBQXdCLEVBQUUsc0JBQXNCLEVBQUUsZUFBZSxFQUFFLE1BQU0saUJBQWlCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBHRU5FUkFURUQgYnkgcHJvamVuIHdhdGNoIC0gRE8gTk9UIEVESVQuXG4vLyBSZWdlbmVyYXRlZCBmcm9tIHRoZSBleHBvcnRpbmcgbW9kdWxlcyBpbiAuL3NyYy5cbi8vIEhhbmQgZWRpdHMgYXJlIG92ZXJ3cml0dGVuIG9uIHRoZSBuZXh0IHdhdGNoOyB0aGlzIGZpbGUgaXMgcmVhZC1vbmx5LlxuXG5leHBvcnQgKiBhcyBzZWFyY2ggZnJvbSBcIi4vc3JjL3NlYXJjaC50c1wiO1xuZXhwb3J0IHsgc2VhcmNoTW9kZVNjaGVtYSwgc2VhcmNoUmVxdWVzdFNjaGVtYSwgc2VhcmNoSGl0U2NoZW1hLCBzZWFyY2hSZXN1bHRTY2hlbWEsIHVuaXZlcnNhbFNlYXJjaFJlcXVlc3RTY2hlbWEsIHNlYXJjaERvY3VtZW50U2NoZW1hLCB1cHNlcnRSZXN1bHRTY2hlbWEsIHNlYXJjaEluZGV4SW5mb1NjaGVtYSwgc2VhcmNoQ2xpZW50Q29uZmlnU2NoZW1hLCBjcmVhdGVJbmRleFJlcXVlc3RTY2hlbWEsIHN5bmNJbmRleFJlcXVlc3RTY2hlbWEsIGluZGV4SW5mb1NjaGVtYSB9IGZyb20gXCIuL3NyYy9zZWFyY2gudHNcIjtcbmV4cG9ydCB0eXBlIHsgU2VhcmNoTW9kZSwgU2VhcmNoUmVxdWVzdCwgU2VhcmNoSGl0LCBTZWFyY2hSZXN1bHQsIFVuaXZlcnNhbFNlYXJjaFJlcXVlc3QsIFNlYXJjaERvY3VtZW50LCBVcHNlcnRSZXN1bHQsIFNlYXJjaEluZGV4SW5mbywgU2VhcmNoQ2xpZW50Q29uZmlnLCBDcmVhdGVJbmRleFJlcXVlc3QsIFN5bmNJbmRleFJlcXVlc3QsIEluZGV4SW5mbyB9IGZyb20gXCIuL3NyYy9zZWFyY2gudHNcIjtcbiJdfQ==
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-format contract for the AI Search add-on: the query a caller (a UI
|
|
3
|
+
* search box, a Mastra tool, a universal-search route) sends and the hits it
|
|
4
|
+
* gets back, plus the document shape used to add / update index contents.
|
|
5
|
+
*
|
|
6
|
+
* Pure (zod + inferred types, no Node-only imports) so the server-side client
|
|
7
|
+
* in `@dbx-tools/search`, the Mastra tool, the `POST /api/search` route,
|
|
8
|
+
* and the React search box all validate / type against ONE definition.
|
|
9
|
+
*
|
|
10
|
+
* "AI Search" is Databricks' current name for Vector Search: an index built
|
|
11
|
+
* from a Delta table (or written directly) that answers a natural-language
|
|
12
|
+
* query with the most relevant rows, combining vector similarity with BM25
|
|
13
|
+
* keyword matching (hybrid). This contract is index-agnostic - it names the
|
|
14
|
+
* index by its Unity Catalog path and returns hits as `{ id, score, fields }`
|
|
15
|
+
* so the same shape serves an autocomplete box, a docs lookup, and a
|
|
16
|
+
* federated search across many indexes.
|
|
17
|
+
*
|
|
18
|
+
* Array fields intentionally avoid `.min()` / `.nonempty()`: those emit
|
|
19
|
+
* `minItems` in the JSON schema, which some Model Serving endpoints reject
|
|
20
|
+
* when the schema is forwarded as a tool definition.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import { z } from "zod";
|
|
25
|
+
/**
|
|
26
|
+
* How a query is matched. `hybrid` (the default) fuses vector similarity with
|
|
27
|
+
* BM25 keyword ranking - the right choice for most search boxes and for source
|
|
28
|
+
* data with exact identifiers (SKUs, error codes). `vector` is pure semantic
|
|
29
|
+
* similarity; `keyword` is pure BM25 full-text (Beta on Databricks). The
|
|
30
|
+
* value maps onto the serving API's `query_type` (`ANN` / `HYBRID`).
|
|
31
|
+
*/
|
|
32
|
+
export declare const searchModeSchema: z.ZodEnum<{
|
|
33
|
+
hybrid: "hybrid";
|
|
34
|
+
vector: "vector";
|
|
35
|
+
keyword: "keyword";
|
|
36
|
+
}>;
|
|
37
|
+
/** How a query is matched (see {@link searchModeSchema}). */
|
|
38
|
+
export type SearchMode = z.infer<typeof searchModeSchema>;
|
|
39
|
+
/** Schema for a search request against a single index. */
|
|
40
|
+
export declare const searchRequestSchema: z.ZodObject<{
|
|
41
|
+
query: z.ZodString;
|
|
42
|
+
index: z.ZodOptional<z.ZodString>;
|
|
43
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
45
|
+
hybrid: "hybrid";
|
|
46
|
+
vector: "vector";
|
|
47
|
+
keyword: "keyword";
|
|
48
|
+
}>>;
|
|
49
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
50
|
+
filter: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
51
|
+
scoreThreshold: z.ZodOptional<z.ZodNumber>;
|
|
52
|
+
}, z.core.$strip>;
|
|
53
|
+
/** A validated search request ({@link searchRequestSchema}). */
|
|
54
|
+
export type SearchRequest = z.infer<typeof searchRequestSchema>;
|
|
55
|
+
/** Schema for a single search hit. */
|
|
56
|
+
export declare const searchHitSchema: z.ZodObject<{
|
|
57
|
+
id: z.ZodString;
|
|
58
|
+
score: z.ZodNumber;
|
|
59
|
+
fields: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
60
|
+
index: z.ZodOptional<z.ZodString>;
|
|
61
|
+
}, z.core.$strip>;
|
|
62
|
+
/** A single search hit ({@link searchHitSchema}). */
|
|
63
|
+
export type SearchHit = z.infer<typeof searchHitSchema>;
|
|
64
|
+
/** Schema for the result of a search. */
|
|
65
|
+
export declare const searchResultSchema: z.ZodObject<{
|
|
66
|
+
query: z.ZodString;
|
|
67
|
+
hits: z.ZodArray<z.ZodObject<{
|
|
68
|
+
id: z.ZodString;
|
|
69
|
+
score: z.ZodNumber;
|
|
70
|
+
fields: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
71
|
+
index: z.ZodOptional<z.ZodString>;
|
|
72
|
+
}, z.core.$strip>>;
|
|
73
|
+
index: z.ZodOptional<z.ZodString>;
|
|
74
|
+
count: z.ZodNumber;
|
|
75
|
+
}, z.core.$strip>;
|
|
76
|
+
/** The outcome of a search ({@link searchResultSchema}). */
|
|
77
|
+
export type SearchResult = z.infer<typeof searchResultSchema>;
|
|
78
|
+
/** Schema for a universal (federated) search across several indexes at once. */
|
|
79
|
+
export declare const universalSearchRequestSchema: z.ZodObject<{
|
|
80
|
+
query: z.ZodString;
|
|
81
|
+
indexes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
82
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
83
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
84
|
+
hybrid: "hybrid";
|
|
85
|
+
vector: "vector";
|
|
86
|
+
keyword: "keyword";
|
|
87
|
+
}>>;
|
|
88
|
+
}, z.core.$strip>;
|
|
89
|
+
/** A validated universal-search request ({@link universalSearchRequestSchema}). */
|
|
90
|
+
export type UniversalSearchRequest = z.infer<typeof universalSearchRequestSchema>;
|
|
91
|
+
/** Schema for a document written to a direct-access index. */
|
|
92
|
+
export declare const searchDocumentSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
93
|
+
/** A document written to an index ({@link searchDocumentSchema}). */
|
|
94
|
+
export type SearchDocument = z.infer<typeof searchDocumentSchema>;
|
|
95
|
+
/** Schema for the result of adding / updating documents. */
|
|
96
|
+
export declare const upsertResultSchema: z.ZodObject<{
|
|
97
|
+
index: z.ZodString;
|
|
98
|
+
count: z.ZodNumber;
|
|
99
|
+
}, z.core.$strip>;
|
|
100
|
+
/** The outcome of an add-documents call ({@link upsertResultSchema}). */
|
|
101
|
+
export type UpsertResult = z.infer<typeof upsertResultSchema>;
|
|
102
|
+
/**
|
|
103
|
+
* Schema for the boot-time index catalogue a UI reads to populate a search box
|
|
104
|
+
* (the payload of the plugin's client config / `GET /api/search/indexes`). An
|
|
105
|
+
* entry names an index, an optional short alias, and the columns worth
|
|
106
|
+
* showing, so a search box needs no server round-trip to render.
|
|
107
|
+
*/
|
|
108
|
+
export declare const searchIndexInfoSchema: z.ZodObject<{
|
|
109
|
+
name: z.ZodString;
|
|
110
|
+
alias: z.ZodOptional<z.ZodString>;
|
|
111
|
+
primaryKey: z.ZodOptional<z.ZodString>;
|
|
112
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
113
|
+
isDefault: z.ZodOptional<z.ZodBoolean>;
|
|
114
|
+
}, z.core.$strip>;
|
|
115
|
+
/** One entry in the index catalogue ({@link searchIndexInfoSchema}). */
|
|
116
|
+
export type SearchIndexInfo = z.infer<typeof searchIndexInfoSchema>;
|
|
117
|
+
/** Schema for the plugin's client config surfaced to a UI. */
|
|
118
|
+
export declare const searchClientConfigSchema: z.ZodObject<{
|
|
119
|
+
indexes: z.ZodArray<z.ZodObject<{
|
|
120
|
+
name: z.ZodString;
|
|
121
|
+
alias: z.ZodOptional<z.ZodString>;
|
|
122
|
+
primaryKey: z.ZodOptional<z.ZodString>;
|
|
123
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
124
|
+
isDefault: z.ZodOptional<z.ZodBoolean>;
|
|
125
|
+
}, z.core.$strip>>;
|
|
126
|
+
defaultIndex: z.ZodOptional<z.ZodString>;
|
|
127
|
+
pageSize: z.ZodNumber;
|
|
128
|
+
basePath: z.ZodString;
|
|
129
|
+
}, z.core.$strip>;
|
|
130
|
+
/** The plugin's client config ({@link searchClientConfigSchema}). */
|
|
131
|
+
export type SearchClientConfig = z.infer<typeof searchClientConfigSchema>;
|
|
132
|
+
/**
|
|
133
|
+
* Schema for creating an index (the `create_index` tool input / the
|
|
134
|
+
* `POST /api/search/index` body). Two shapes, both inferring aggressively:
|
|
135
|
+
* pass `sourceTable` for a Delta Sync index (Databricks computes embeddings and
|
|
136
|
+
* keeps it synced) or `embeddingDimension` for a direct-access index you write
|
|
137
|
+
* vectors to yourself.
|
|
138
|
+
*/
|
|
139
|
+
export declare const createIndexRequestSchema: z.ZodObject<{
|
|
140
|
+
name: z.ZodString;
|
|
141
|
+
sourceTable: z.ZodOptional<z.ZodString>;
|
|
142
|
+
primaryKey: z.ZodOptional<z.ZodString>;
|
|
143
|
+
embeddingSourceColumn: z.ZodOptional<z.ZodString>;
|
|
144
|
+
embeddingModel: z.ZodOptional<z.ZodString>;
|
|
145
|
+
endpoint: z.ZodOptional<z.ZodString>;
|
|
146
|
+
embeddingDimension: z.ZodOptional<z.ZodNumber>;
|
|
147
|
+
pipelineType: z.ZodOptional<z.ZodEnum<{
|
|
148
|
+
TRIGGERED: "TRIGGERED";
|
|
149
|
+
CONTINUOUS: "CONTINUOUS";
|
|
150
|
+
}>>;
|
|
151
|
+
columnsToSync: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
152
|
+
}, z.core.$strip>;
|
|
153
|
+
/** A validated create-index request ({@link createIndexRequestSchema}). */
|
|
154
|
+
export type CreateIndexRequest = z.infer<typeof createIndexRequestSchema>;
|
|
155
|
+
/** Schema for the `sync_index` tool input / the `POST /api/search/index/sync` body. */
|
|
156
|
+
export declare const syncIndexRequestSchema: z.ZodObject<{
|
|
157
|
+
index: z.ZodOptional<z.ZodString>;
|
|
158
|
+
}, z.core.$strip>;
|
|
159
|
+
/** A validated sync-index request ({@link syncIndexRequestSchema}). */
|
|
160
|
+
export type SyncIndexRequest = z.infer<typeof syncIndexRequestSchema>;
|
|
161
|
+
/** Schema for a resolved index definition (the create/get result). */
|
|
162
|
+
export declare const indexInfoSchema: z.ZodObject<{
|
|
163
|
+
name: z.ZodString;
|
|
164
|
+
endpoint: z.ZodOptional<z.ZodString>;
|
|
165
|
+
primaryKey: z.ZodOptional<z.ZodString>;
|
|
166
|
+
columns: z.ZodArray<z.ZodString>;
|
|
167
|
+
ready: z.ZodBoolean;
|
|
168
|
+
rowCount: z.ZodOptional<z.ZodNumber>;
|
|
169
|
+
}, z.core.$strip>;
|
|
170
|
+
/** A resolved index definition ({@link indexInfoSchema}). */
|
|
171
|
+
export type IndexInfo = z.infer<typeof indexInfoSchema>;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-format contract for the AI Search add-on: the query a caller (a UI
|
|
3
|
+
* search box, a Mastra tool, a universal-search route) sends and the hits it
|
|
4
|
+
* gets back, plus the document shape used to add / update index contents.
|
|
5
|
+
*
|
|
6
|
+
* Pure (zod + inferred types, no Node-only imports) so the server-side client
|
|
7
|
+
* in `@dbx-tools/search`, the Mastra tool, the `POST /api/search` route,
|
|
8
|
+
* and the React search box all validate / type against ONE definition.
|
|
9
|
+
*
|
|
10
|
+
* "AI Search" is Databricks' current name for Vector Search: an index built
|
|
11
|
+
* from a Delta table (or written directly) that answers a natural-language
|
|
12
|
+
* query with the most relevant rows, combining vector similarity with BM25
|
|
13
|
+
* keyword matching (hybrid). This contract is index-agnostic - it names the
|
|
14
|
+
* index by its Unity Catalog path and returns hits as `{ id, score, fields }`
|
|
15
|
+
* so the same shape serves an autocomplete box, a docs lookup, and a
|
|
16
|
+
* federated search across many indexes.
|
|
17
|
+
*
|
|
18
|
+
* Array fields intentionally avoid `.min()` / `.nonempty()`: those emit
|
|
19
|
+
* `minItems` in the JSON schema, which some Model Serving endpoints reject
|
|
20
|
+
* when the schema is forwarded as a tool definition.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import { z } from "zod";
|
|
25
|
+
/**
|
|
26
|
+
* How a query is matched. `hybrid` (the default) fuses vector similarity with
|
|
27
|
+
* BM25 keyword ranking - the right choice for most search boxes and for source
|
|
28
|
+
* data with exact identifiers (SKUs, error codes). `vector` is pure semantic
|
|
29
|
+
* similarity; `keyword` is pure BM25 full-text (Beta on Databricks). The
|
|
30
|
+
* value maps onto the serving API's `query_type` (`ANN` / `HYBRID`).
|
|
31
|
+
*/
|
|
32
|
+
export const searchModeSchema = z
|
|
33
|
+
.enum(["hybrid", "vector", "keyword"])
|
|
34
|
+
.describe("Match strategy: 'hybrid' fuses semantic similarity with keyword ranking (default, best for most searches), 'vector' is pure semantic, 'keyword' is pure full-text.");
|
|
35
|
+
/** Schema for a search request against a single index. */
|
|
36
|
+
export const searchRequestSchema = z.object({
|
|
37
|
+
query: z
|
|
38
|
+
.string()
|
|
39
|
+
.describe("What to search for, as natural language or keywords. Prefix fragments work for autocomplete."),
|
|
40
|
+
index: z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Unity Catalog name of the index to search (catalog.schema.index). Defaults to the plugin's configured index. Accepts a short alias when one is configured."),
|
|
44
|
+
limit: z
|
|
45
|
+
.number()
|
|
46
|
+
.int()
|
|
47
|
+
.positive()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Maximum number of hits to return. Defaults to the plugin's configured page size."),
|
|
50
|
+
mode: searchModeSchema.optional(),
|
|
51
|
+
columns: z
|
|
52
|
+
.array(z.string())
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("Which document columns to return per hit. Defaults to the plugin's configured columns (or all indexed columns)."),
|
|
55
|
+
filter: z
|
|
56
|
+
.record(z.string(), z.unknown())
|
|
57
|
+
.optional()
|
|
58
|
+
.describe('Attribute filters as { column: value } (a value, or an operator map like { ">=": 10 }). Combined with AND. Maps onto the index `filters_json`.'),
|
|
59
|
+
scoreThreshold: z
|
|
60
|
+
.number()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("Drop hits whose relevance score is below this value."),
|
|
63
|
+
});
|
|
64
|
+
/** Schema for a single search hit. */
|
|
65
|
+
export const searchHitSchema = z.object({
|
|
66
|
+
id: z.string().describe("The document's primary-key value."),
|
|
67
|
+
score: z.number().describe("Relevance score; higher is more relevant."),
|
|
68
|
+
fields: z
|
|
69
|
+
.record(z.string(), z.unknown())
|
|
70
|
+
.describe("The returned columns for this document, keyed by column name."),
|
|
71
|
+
index: z
|
|
72
|
+
.string()
|
|
73
|
+
.optional()
|
|
74
|
+
.describe("Which index the hit came from (set on universal/federated results)."),
|
|
75
|
+
});
|
|
76
|
+
/** Schema for the result of a search. */
|
|
77
|
+
export const searchResultSchema = z.object({
|
|
78
|
+
query: z.string().describe("Echo of the query that was searched."),
|
|
79
|
+
hits: z.array(searchHitSchema).describe("Matching documents, most relevant first."),
|
|
80
|
+
index: z.string().optional().describe("The index searched (omitted for a universal search)."),
|
|
81
|
+
count: z.number().int().describe("Number of hits returned."),
|
|
82
|
+
});
|
|
83
|
+
/** Schema for a universal (federated) search across several indexes at once. */
|
|
84
|
+
export const universalSearchRequestSchema = z.object({
|
|
85
|
+
query: z.string().describe("What to search for across every configured index."),
|
|
86
|
+
indexes: z
|
|
87
|
+
.array(z.string())
|
|
88
|
+
.optional()
|
|
89
|
+
.describe("Which indexes to search. Defaults to every index the plugin knows about."),
|
|
90
|
+
limit: z
|
|
91
|
+
.number()
|
|
92
|
+
.int()
|
|
93
|
+
.positive()
|
|
94
|
+
.optional()
|
|
95
|
+
.describe("Maximum hits PER index before results are merged and re-ranked."),
|
|
96
|
+
mode: searchModeSchema.optional(),
|
|
97
|
+
});
|
|
98
|
+
/** Schema for a document written to a direct-access index. */
|
|
99
|
+
export const searchDocumentSchema = z
|
|
100
|
+
.record(z.string(), z.unknown())
|
|
101
|
+
.describe("A document as { column: value }. Must include the index primary-key column.");
|
|
102
|
+
/** Schema for the result of adding / updating documents. */
|
|
103
|
+
export const upsertResultSchema = z.object({
|
|
104
|
+
index: z.string().describe("The index the documents were written to."),
|
|
105
|
+
count: z.number().int().describe("Number of documents accepted."),
|
|
106
|
+
});
|
|
107
|
+
/**
|
|
108
|
+
* Schema for the boot-time index catalogue a UI reads to populate a search box
|
|
109
|
+
* (the payload of the plugin's client config / `GET /api/search/indexes`). An
|
|
110
|
+
* entry names an index, an optional short alias, and the columns worth
|
|
111
|
+
* showing, so a search box needs no server round-trip to render.
|
|
112
|
+
*/
|
|
113
|
+
export const searchIndexInfoSchema = z.object({
|
|
114
|
+
name: z.string().describe("Unity Catalog name of the index (catalog.schema.index)."),
|
|
115
|
+
alias: z.string().optional().describe("Short alias the plugin accepts in place of `name`."),
|
|
116
|
+
primaryKey: z.string().optional().describe("The index's primary-key column, when known."),
|
|
117
|
+
columns: z
|
|
118
|
+
.array(z.string())
|
|
119
|
+
.optional()
|
|
120
|
+
.describe("Columns returned by default, in display order."),
|
|
121
|
+
isDefault: z.boolean().optional().describe("True for the plugin's default index."),
|
|
122
|
+
});
|
|
123
|
+
/** Schema for the plugin's client config surfaced to a UI. */
|
|
124
|
+
export const searchClientConfigSchema = z.object({
|
|
125
|
+
indexes: z.array(searchIndexInfoSchema).describe("Indexes the search UI may query."),
|
|
126
|
+
defaultIndex: z.string().optional().describe("The index a search box selects by default."),
|
|
127
|
+
pageSize: z.number().int().describe("Default number of hits a search returns."),
|
|
128
|
+
basePath: z
|
|
129
|
+
.string()
|
|
130
|
+
.describe("Base path the search routes are mounted under (e.g. /api/search)."),
|
|
131
|
+
});
|
|
132
|
+
/**
|
|
133
|
+
* Schema for creating an index (the `create_index` tool input / the
|
|
134
|
+
* `POST /api/search/index` body). Two shapes, both inferring aggressively:
|
|
135
|
+
* pass `sourceTable` for a Delta Sync index (Databricks computes embeddings and
|
|
136
|
+
* keeps it synced) or `embeddingDimension` for a direct-access index you write
|
|
137
|
+
* vectors to yourself.
|
|
138
|
+
*/
|
|
139
|
+
export const createIndexRequestSchema = z.object({
|
|
140
|
+
name: z.string().describe("Unity Catalog name for the new index (catalog.schema.index)."),
|
|
141
|
+
sourceTable: z
|
|
142
|
+
.string()
|
|
143
|
+
.optional()
|
|
144
|
+
.describe("Delta table (catalog.schema.table) to build a synced index from. Provide this OR embeddingDimension."),
|
|
145
|
+
primaryKey: z.string().optional().describe("Primary-key column. Defaults to 'id'."),
|
|
146
|
+
embeddingSourceColumn: z
|
|
147
|
+
.string()
|
|
148
|
+
.optional()
|
|
149
|
+
.describe("Text column embeddings are computed from (Delta Sync). Defaults to the first of text/content/body."),
|
|
150
|
+
embeddingModel: z
|
|
151
|
+
.string()
|
|
152
|
+
.optional()
|
|
153
|
+
.describe("Embedding model endpoint (name or loose name). Defaults to the app's embedding model, or the best embedding endpoint in the workspace."),
|
|
154
|
+
endpoint: z
|
|
155
|
+
.string()
|
|
156
|
+
.optional()
|
|
157
|
+
.describe("Vector Search endpoint to host the index on. Defaults to the app's configured endpoint."),
|
|
158
|
+
embeddingDimension: z
|
|
159
|
+
.number()
|
|
160
|
+
.int()
|
|
161
|
+
.positive()
|
|
162
|
+
.optional()
|
|
163
|
+
.describe("For a direct-access index (no source table): the embedding vector dimension."),
|
|
164
|
+
pipelineType: z
|
|
165
|
+
.enum(["TRIGGERED", "CONTINUOUS"])
|
|
166
|
+
.optional()
|
|
167
|
+
.describe("Delta Sync mode: 'TRIGGERED' (default) syncs on demand; 'CONTINUOUS' keeps the index fresh."),
|
|
168
|
+
columnsToSync: z
|
|
169
|
+
.array(z.string())
|
|
170
|
+
.optional()
|
|
171
|
+
.describe("Extra columns to sync alongside the embedding source column (Delta Sync)."),
|
|
172
|
+
});
|
|
173
|
+
/** Schema for the `sync_index` tool input / the `POST /api/search/index/sync` body. */
|
|
174
|
+
export const syncIndexRequestSchema = z.object({
|
|
175
|
+
index: z
|
|
176
|
+
.string()
|
|
177
|
+
.describe("The index to sync (name or configured alias). Defaults to the app's default index when omitted.")
|
|
178
|
+
.optional(),
|
|
179
|
+
});
|
|
180
|
+
/** Schema for a resolved index definition (the create/get result). */
|
|
181
|
+
export const indexInfoSchema = z.object({
|
|
182
|
+
name: z.string().describe("Unity Catalog name of the index."),
|
|
183
|
+
endpoint: z.string().optional().describe("The Vector Search endpoint hosting the index."),
|
|
184
|
+
primaryKey: z.string().optional().describe("The index's primary-key column."),
|
|
185
|
+
columns: z.array(z.string()).describe("Columns available on the index."),
|
|
186
|
+
ready: z.boolean().describe("True when the index has finished provisioning and can be queried."),
|
|
187
|
+
rowCount: z.number().int().optional().describe("Number of indexed rows, when known."),
|
|
188
|
+
});
|
|
189
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VhcmNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlYXJjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNCRztBQUVILE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFFeEI7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQztLQUM5QixJQUFJLENBQUMsQ0FBQyxRQUFRLEVBQUUsUUFBUSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0tBQ3JDLFFBQVEsQ0FDUCxvS0FBb0ssQ0FDckssQ0FBQztBQUtKLDBEQUEwRDtBQUMxRCxNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQzFDLEtBQUssRUFBRSxDQUFDO1NBQ0wsTUFBTSxFQUFFO1NBQ1IsUUFBUSxDQUNQLDhGQUE4RixDQUMvRjtJQUNILEtBQUssRUFBRSxDQUFDO1NBQ0wsTUFBTSxFQUFFO1NBQ1IsUUFBUSxFQUFFO1NBQ1YsUUFBUSxDQUNQLDRKQUE0SixDQUM3SjtJQUNILEtBQUssRUFBRSxDQUFDO1NBQ0wsTUFBTSxFQUFFO1NBQ1IsR0FBRyxFQUFFO1NBQ0wsUUFBUSxFQUFFO1NBQ1YsUUFBUSxFQUFFO1NBQ1YsUUFBUSxDQUFDLGtGQUFrRixDQUFDO0lBQy9GLElBQUksRUFBRSxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUU7SUFDakMsT0FBTyxFQUFFLENBQUM7U0FDUCxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDO1NBQ2pCLFFBQVEsRUFBRTtTQUNWLFFBQVEsQ0FDUCxpSEFBaUgsQ0FDbEg7SUFDSCxNQUFNLEVBQUUsQ0FBQztTQUNOLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1NBQy9CLFFBQVEsRUFBRTtTQUNWLFFBQVEsQ0FDUCxnSkFBZ0os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QUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxrQ0FBa0MsQ0FBQztJQUM3RCxRQUFRLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsQ0FBQywrQ0FBK0MsQ0FBQztJQUN6RixVQUFVLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsQ0FBQyxpQ0FBaUMsQ0FBQztJQUM3RSxPQUFPLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsaUNBQWlDLENBQUM7SUFDeEUsS0FBSyxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUMsbUVBQW1FLENBQUM7SUFDaEcsUUFBUSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMscUNBQXFDLENBQUM7Q0FDdEYsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBXaXJlLWZvcm1hdCBjb250cmFjdCBmb3IgdGhlIEFJIFNlYXJjaCBhZGQtb246IHRoZSBxdWVyeSBhIGNhbGxlciAoYSBVSVxuICogc2VhcmNoIGJveCwgYSBNYXN0cmEgdG9vbCwgYSB1bml2ZXJzYWwtc2VhcmNoIHJvdXRlKSBzZW5kcyBhbmQgdGhlIGhpdHMgaXRcbiAqIGdldHMgYmFjaywgcGx1cyB0aGUgZG9jdW1lbnQgc2hhcGUgdXNlZCB0byBhZGQgLyB1cGRhdGUgaW5kZXggY29udGVudHMuXG4gKlxuICogUHVyZSAoem9kICsgaW5mZXJyZWQgdHlwZXMsIG5vIE5vZGUtb25seSBpbXBvcnRzKSBzbyB0aGUgc2VydmVyLXNpZGUgY2xpZW50XG4gKiBpbiBgQGRieC10b29scy9zZWFyY2hgLCB0aGUgTWFzdHJhIHRvb2wsIHRoZSBgUE9TVCAvYXBpL3NlYXJjaGAgcm91dGUsXG4gKiBhbmQgdGhlIFJlYWN0IHNlYXJjaCBib3ggYWxsIHZhbGlkYXRlIC8gdHlwZSBhZ2FpbnN0IE9ORSBkZWZpbml0aW9uLlxuICpcbiAqIFwiQUkgU2VhcmNoXCIgaXMgRGF0YWJyaWNrcycgY3VycmVudCBuYW1lIGZvciBWZWN0b3IgU2VhcmNoOiBhbiBpbmRleCBidWlsdFxuICogZnJvbSBhIERlbHRhIHRhYmxlIChvciB3cml0dGVuIGRpcmVjdGx5KSB0aGF0IGFuc3dlcnMgYSBuYXR1cmFsLWxhbmd1YWdlXG4gKiBxdWVyeSB3aXRoIHRoZSBtb3N0IHJlbGV2YW50IHJvd3MsIGNvbWJpbmluZyB2ZWN0b3Igc2ltaWxhcml0eSB3aXRoIEJNMjVcbiAqIGtleXdvcmQgbWF0Y2hpbmcgKGh5YnJpZCkuIFRoaXMgY29udHJhY3QgaXMgaW5kZXgtYWdub3N0aWMgLSBpdCBuYW1lcyB0aGVcbiAqIGluZGV4IGJ5IGl0cyBVbml0eSBDYXRhbG9nIHBhdGggYW5kIHJldHVybnMgaGl0cyBhcyBgeyBpZCwgc2NvcmUsIGZpZWxkcyB9YFxuICogc28gdGhlIHNhbWUgc2hhcGUgc2VydmVzIGFuIGF1dG9jb21wbGV0ZSBib3gsIGEgZG9jcyBsb29rdXAsIGFuZCBhXG4gKiBmZWRlcmF0ZWQgc2VhcmNoIGFjcm9zcyBtYW55IGluZGV4ZXMuXG4gKlxuICogQXJyYXkgZmllbGRzIGludGVudGlvbmFsbHkgYXZvaWQgYC5taW4oKWAgLyBgLm5vbmVtcHR5KClgOiB0aG9zZSBlbWl0XG4gKiBgbWluSXRlbXNgIGluIHRoZSBKU09OIHNjaGVtYSwgd2hpY2ggc29tZSBNb2RlbCBTZXJ2aW5nIGVuZHBvaW50cyByZWplY3RcbiAqIHdoZW4gdGhlIHNjaGVtYSBpcyBmb3J3YXJkZWQgYXMgYSB0b29sIGRlZmluaXRpb24uXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5cbi8qKlxuICogSG93IGEgcXVlcnkgaXMgbWF0Y2hlZC4gYGh5YnJpZGAgKHRoZSBkZWZhdWx0KSBmdXNlcyB2ZWN0b3Igc2ltaWxhcml0eSB3aXRoXG4gKiBCTTI1IGtleXdvcmQgcmFua2luZyAtIHRoZSByaWdodCBjaG9pY2UgZm9yIG1vc3Qgc2VhcmNoIGJveGVzIGFuZCBmb3Igc291cmNlXG4gKiBkYXRhIHdpdGggZXhhY3QgaWRlbnRpZmllcnMgKFNLVXMsIGVycm9yIGNvZGVzKS4gYHZlY3RvcmAgaXMgcHVyZSBzZW1hbnRpY1xuICogc2ltaWxhcml0eTsgYGtleXdvcmRgIGlzIHB1cmUgQk0yNSBmdWxsLXRleHQgKEJldGEgb24gRGF0YWJyaWNrcykuIFRoZVxuICogdmFsdWUgbWFwcyBvbnRvIHRoZSBzZXJ2aW5nIEFQSSdzIGBxdWVyeV90eXBlYCAoYEFOTmAgLyBgSFlCUklEYCkuXG4gKi9cbmV4cG9ydCBjb25zdCBzZWFyY2hNb2RlU2NoZW1hID0gelxuICAuZW51bShbXCJoeWJyaWRcIiwgXCJ2ZWN0b3JcIiwgXCJrZXl3b3JkXCJdKVxuICAuZGVzY3JpYmUoXG4gICAgXCJNYXRjaCBzdHJhdGVneTogJ2h5YnJpZCcgZnVzZXMgc2VtYW50aWMgc2ltaWxhcml0eSB3aXRoIGtleXdvcmQgcmFua2luZyAoZGVmYXVsdCwgYmVzdCBmb3IgbW9zdCBzZWFyY2hlcyksICd2ZWN0b3InIGlzIHB1cmUgc2VtYW50aWMsICdrZXl3b3JkJyBpcyBwdXJlIGZ1bGwtdGV4dC5cIixcbiAgKTtcblxuLyoqIEhvdyBhIHF1ZXJ5IGlzIG1hdGNoZWQgKHNlZSB7QGxpbmsgc2VhcmNoTW9kZVNjaGVtYX0pLiAqL1xuZXhwb3J0IHR5cGUgU2VhcmNoTW9kZSA9IHouaW5mZXI8dHlwZW9mIHNlYXJjaE1vZGVTY2hlbWE+O1xuXG4vKiogU2NoZW1hIGZvciBhIHNlYXJjaCByZXF1ZXN0IGFnYWluc3QgYSBzaW5nbGUgaW5kZXguICovXG5leHBvcnQgY29uc3Qgc2VhcmNoUmVxdWVzdFNjaGVtYSA9IHoub2JqZWN0KHtcbiAgcXVlcnk6IHpcbiAgICAuc3RyaW5nKClcbiAgICAuZGVzY3JpYmUoXG4gICAgICBcIldoYXQgdG8gc2VhcmNoIGZvciwgYXMgbmF0dXJhbCBsYW5ndWFnZSBvciBrZXl3b3Jkcy4gUHJlZml4IGZyYWdtZW50cyB3b3JrIGZvciBhdXRvY29tcGxldGUuXCIsXG4gICAgKSxcbiAgaW5kZXg6IHpcbiAgICAuc3RyaW5nKClcbiAgICAub3B0aW9uYWwoKVxuICAgIC5kZXNjcmliZShcbiAgICAgIFwiVW5pdHkgQ2F0YWxvZyBuYW1lIG9mIHRoZSBpbmRleCB0byBzZWFyY2ggKGNhdGFsb2cuc2NoZW1hLmluZGV4KS4gRGVmYXVsdHMgdG8gdGhlIHBsdWdpbidzIGNvbmZpZ3VyZWQgaW5kZXguIEFjY2VwdHMgYSBzaG9ydCBhbGlhcyB3aGVuIG9uZSBpcyBjb25maWd1cmVkLlwiLFxuICAgICksXG4gIGxpbWl0OiB6XG4gICAgLm51bWJlcigpXG4gICAgLmludCgpXG4gICAgLnBvc2l0aXZlKClcbiAgICAub3B0aW9uYWwoKVxuICAgIC5kZXNjcmliZShcIk1heGltdW0gbnVtYmVyIG9mIGhpdHMgdG8gcmV0dXJuLiBEZWZhdWx0cyB0byB0aGUgcGx1Z2luJ3MgY29uZmlndXJlZCBwYWdlIHNpemUuXCIpLFxuICBtb2RlOiBzZWFyY2hNb2RlU2NoZW1hLm9wdGlvbmFsKCksXG4gIGNvbHVtbnM6IHpcbiAgICAuYXJyYXkoei5zdHJpbmcoKSlcbiAgICAub3B0aW9uYWwoKVxuICAgIC5kZXNjcmliZShcbiAgICAgIFwiV2hpY2ggZG9jdW1lbnQgY29sdW1ucyB0byByZXR1cm4gcGVyIGhpdC4gRGVmYXVsdHMgdG8gdGhlIHBsdWdpbidzIGNvbmZpZ3VyZWQgY29sdW1ucyAob3IgYWxsIGluZGV4ZWQgY29sdW1ucykuXCIsXG4gICAgKSxcbiAgZmlsdGVyOiB6XG4gICAgLnJlY29yZCh6LnN0cmluZygpLCB6LnVua25vd24oKSlcbiAgICAub3B0aW9uYWwoKVxuICAgIC5kZXNjcmliZShcbiAgICAgICdBdHRyaWJ1dGUgZmlsdGVycyBhcyB7IGNvbHVtbjogdmFsdWUgfSAoYSB2YWx1ZSwgb3IgYW4gb3BlcmF0b3IgbWFwIGxpa2UgeyBcIj49XCI6IDEwIH0pLiBDb21iaW5lZCB3aXRoIEFORC4gTWFwcyBvbnRvIHRoZSBpbmRleCBgZmlsdGVyc19qc29uYC4nLFxuICAgICksXG4gIHNjb3JlVGhyZXNob2xkOiB6XG4gICAgLm51bWJlcigpXG4gICAgLm9wdGlvbmFsKClcbiAgICAuZGVzY3JpYmUoXCJEcm9wIGhpdHMgd2hvc2UgcmVsZXZhbmNlIHNjb3JlIGlzIGJlbG93IHRoaXMgdmFsdWUuXCIpLFxufSk7XG5cbi8qKiBBIHZhbGlkYXRlZCBzZWFyY2ggcmVxdWVzdCAoe0BsaW5rIHNlYXJjaFJlcXVlc3RTY2hlbWF9KS4gKi9cbmV4cG9ydCB0eXBlIFNlYXJjaFJlcXVlc3QgPSB6LmluZmVyPHR5cGVvZiBzZWFyY2hSZXF1ZXN0U2NoZW1hPjtcblxuLyoqIFNjaGVtYSBmb3IgYSBzaW5nbGUgc2VhcmNoIGhpdC4gKi9cbmV4cG9ydCBjb25zdCBzZWFyY2hIaXRTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIGlkOiB6LnN0cmluZygpLmRlc2NyaWJlKFwiVGhlIGRvY3VtZW50J3MgcHJpbWFyeS1rZXkgdmFsdWUuXCIpLFxuICBzY29yZTogei5udW1iZXIoKS5kZXNjcmliZShcIlJlbGV2YW5jZSBzY29yZTsgaGlnaGVyIGlzIG1vcmUgcmVsZXZhbnQuXCIpLFxuICBmaWVsZHM6IHpcbiAgICAucmVjb3JkKHouc3RyaW5nKCksIHoudW5rbm93bigpKVxuICAgIC5kZXNjcmliZShcIlRoZSByZXR1cm5lZCBjb2x1bW5zIGZvciB0aGlzIGRvY3VtZW50LCBrZXllZCBieSBjb2x1bW4gbmFtZS5cIiksXG4gIGluZGV4OiB6XG4gICAgLnN0cmluZygpXG4gICAgLm9wdGlvbmFsKClcbiAgICAuZGVzY3JpYmUoXCJXaGljaCBpbmRleCB0aGUgaGl0IGNhbWUgZnJvbSAoc2V0IG9uIHVuaXZlcnNhbC9mZWRlcmF0ZWQgcmVzdWx0cykuXCIpLFxufSk7XG5cbi8qKiBBIHNpbmdsZSBzZWFyY2ggaGl0ICh7QGxpbmsgc2VhcmNoSGl0U2NoZW1hfSkuICovXG5leHBvcnQgdHlwZSBTZWFyY2hIaXQgPSB6LmluZmVyPHR5cGVvZiBzZWFyY2hIaXRTY2hlbWE+O1xuXG4vKiogU2NoZW1hIGZvciB0aGUgcmVzdWx0IG9mIGEgc2VhcmNoLiAqL1xuZXhwb3J0IGNvbnN0IHNlYXJjaFJlc3VsdFNjaGVtYSA9IHoub2JqZWN0KHtcbiAgcXVlcnk6IHouc3RyaW5nKCkuZGVzY3JpYmUoXCJFY2hvIG9mIHRoZSBxdWVyeSB0aGF0IHdhcyBzZWFyY2hlZC5cIiksXG4gIGhpdHM6IHouYXJyYXkoc2VhcmNoSGl0U2NoZW1hKS5kZXNjcmliZShcIk1hdGNoaW5nIGRvY3VtZW50cywgbW9zdCByZWxldmFudCBmaXJzdC5cIiksXG4gIGluZGV4OiB6LnN0cmluZygpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJUaGUgaW5kZXggc2VhcmNoZWQgKG9taXR0ZWQgZm9yIGEgdW5pdmVyc2FsIHNlYXJjaCkuXCIpLFxuICBjb3VudDogei5udW1iZXIoKS5pbnQoKS5kZXNjcmliZShcIk51bWJlciBvZiBoaXRzIHJldHVybmVkLlwiKSxcbn0pO1xuXG4vKiogVGhlIG91dGNvbWUgb2YgYSBzZWFyY2ggKHtAbGluayBzZWFyY2hSZXN1bHRTY2hlbWF9KS4gKi9cbmV4cG9ydCB0eXBlIFNlYXJjaFJlc3VsdCA9IHouaW5mZXI8dHlwZW9mIHNlYXJjaFJlc3VsdFNjaGVtYT47XG5cbi8qKiBTY2hlbWEgZm9yIGEgdW5pdmVyc2FsIChmZWRlcmF0ZWQpIHNlYXJjaCBhY3Jvc3Mgc2V2ZXJhbCBpbmRleGVzIGF0IG9uY2UuICovXG5leHBvcnQgY29uc3QgdW5pdmVyc2FsU2VhcmNoUmVxdWVzdFNjaGVtYSA9IHoub2JqZWN0KHtcbiAgcXVlcnk6IHouc3RyaW5nKCkuZGVzY3JpYmUoXCJXaGF0IHRvIHNlYXJjaCBmb3IgYWNyb3NzIGV2ZXJ5IGNvbmZpZ3VyZWQgaW5kZXguXCIpLFxuICBpbmRleGVzOiB6XG4gICAgLmFycmF5KHouc3RyaW5nKCkpXG4gICAgLm9wdGlvbmFsKClcbiAgICAuZGVzY3JpYmUoXCJXaGljaCBpbmRleGVzIHRvIHNlYXJjaC4gRGVmYXVsdHMgdG8gZXZlcnkgaW5kZXggdGhlIHBsdWdpbiBrbm93cyBhYm91dC5cIiksXG4gIGxpbWl0OiB6XG4gICAgLm51bWJlcigpXG4gICAgLmludCgpXG4gICAgLnBvc2l0aXZlKClcbiAgICAub3B0aW9uYWwoKVxuICAgIC5kZXNjcmliZShcIk1heGltdW0gaGl0cyBQRVIgaW5kZXggYmVmb3JlIHJlc3VsdHMgYXJlIG1lcmdlZCBhbmQgcmUtcmFua2VkLlwiKSxcbiAgbW9kZTogc2VhcmNoTW9kZVNjaGVtYS5vcHRpb25hbCgpLFxufSk7XG5cbi8qKiBBIHZhbGlkYXRlZCB1bml2ZXJzYWwtc2VhcmNoIHJlcXVlc3QgKHtAbGluayB1bml2ZXJzYWxTZWFyY2hSZXF1ZXN0U2NoZW1hfSkuICovXG5leHBvcnQgdHlwZSBVbml2ZXJzYWxTZWFyY2hSZXF1ZXN0ID0gei5pbmZlcjx0eXBlb2YgdW5pdmVyc2FsU2VhcmNoUmVxdWVzdFNjaGVtYT47XG5cbi8qKiBTY2hlbWEgZm9yIGEgZG9jdW1lbnQgd3JpdHRlbiB0byBhIGRpcmVjdC1hY2Nlc3MgaW5kZXguICovXG5leHBvcnQgY29uc3Qgc2VhcmNoRG9jdW1lbnRTY2hlbWEgPSB6XG4gIC5yZWNvcmQoei5zdHJpbmcoKSwgei51bmtub3duKCkpXG4gIC5kZXNjcmliZShcIkEgZG9jdW1lbnQgYXMgeyBjb2x1bW46IHZhbHVlIH0uIE11c3QgaW5jbHVkZSB0aGUgaW5kZXggcHJpbWFyeS1rZXkgY29sdW1uLlwiKTtcblxuLyoqIEEgZG9jdW1lbnQgd3JpdHRlbiB0byBhbiBpbmRleCAoe0BsaW5rIHNlYXJjaERvY3VtZW50U2NoZW1hfSkuICovXG5leHBvcnQgdHlwZSBTZWFyY2hEb2N1bWVudCA9IHouaW5mZXI8dHlwZW9mIHNlYXJjaERvY3VtZW50U2NoZW1hPjtcblxuLyoqIFNjaGVtYSBmb3IgdGhlIHJlc3VsdCBvZiBhZGRpbmcgLyB1cGRhdGluZyBkb2N1bWVudHMuICovXG5leHBvcnQgY29uc3QgdXBzZXJ0UmVzdWx0U2NoZW1hID0gei5vYmplY3Qoe1xuICBpbmRleDogei5zdHJpbmcoKS5kZXNjcmliZShcIlRoZSBpbmRleCB0aGUgZG9jdW1lbnRzIHdlcmUgd3JpdHRlbiB0by5cIiksXG4gIGNvdW50OiB6Lm51bWJlcigpLmludCgpLmRlc2NyaWJlKFwiTnVtYmVyIG9mIGRvY3VtZW50cyBhY2NlcHRlZC5cIiksXG59KTtcblxuLyoqIFRoZSBvdXRjb21lIG9mIGFuIGFkZC1kb2N1bWVudHMgY2FsbCAoe0BsaW5rIHVwc2VydFJlc3VsdFNjaGVtYX0pLiAqL1xuZXhwb3J0IHR5cGUgVXBzZXJ0UmVzdWx0ID0gei5pbmZlcjx0eXBlb2YgdXBzZXJ0UmVzdWx0U2NoZW1hPjtcblxuLyoqXG4gKiBTY2hlbWEgZm9yIHRoZSBib290LXRpbWUgaW5kZXggY2F0YWxvZ3VlIGEgVUkgcmVhZHMgdG8gcG9wdWxhdGUgYSBzZWFyY2ggYm94XG4gKiAodGhlIHBheWxvYWQgb2YgdGhlIHBsdWdpbidzIGNsaWVudCBjb25maWcgLyBgR0VUIC9hcGkvc2VhcmNoL2luZGV4ZXNgKS4gQW5cbiAqIGVudHJ5IG5hbWVzIGFuIGluZGV4LCBhbiBvcHRpb25hbCBzaG9ydCBhbGlhcywgYW5kIHRoZSBjb2x1bW5zIHdvcnRoXG4gKiBzaG93aW5nLCBzbyBhIHNlYXJjaCBib3ggbmVlZHMgbm8gc2VydmVyIHJvdW5kLXRyaXAgdG8gcmVuZGVyLlxuICovXG5leHBvcnQgY29uc3Qgc2VhcmNoSW5kZXhJbmZvU2NoZW1hID0gei5vYmplY3Qoe1xuICBuYW1lOiB6LnN0cmluZygpLmRlc2NyaWJlKFwiVW5pdHkgQ2F0YWxvZyBuYW1lIG9mIHRoZSBpbmRleCAoY2F0YWxvZy5zY2hlbWEuaW5kZXgpLlwiKSxcbiAgYWxpYXM6IHouc3RyaW5nKCkub3B0aW9uYWwoKS5kZXNjcmliZShcIlNob3J0IGFsaWFzIHRoZSBwbHVnaW4gYWNjZXB0cyBpbiBwbGFjZSBvZiBgbmFtZWAuXCIpLFxuICBwcmltYXJ5S2V5OiB6LnN0cmluZygpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJUaGUgaW5kZXgncyBwcmltYXJ5LWtleSBjb2x1bW4sIHdoZW4ga25vd24uXCIpLFxuICBjb2x1bW5zOiB6XG4gICAgLmFycmF5KHouc3RyaW5nKCkpXG4gICAgLm9wdGlvbmFsKClcbiAgICAuZGVzY3JpYmUoXCJDb2x1bW5zIHJldHVybmVkIGJ5IGRlZmF1bHQsIGluIGRpc3BsYXkgb3JkZXIuXCIpLFxuICBpc0RlZmF1bHQ6IHouYm9vbGVhbigpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJUcnVlIGZvciB0aGUgcGx1Z2luJ3MgZGVmYXVsdCBpbmRleC5cIiksXG59KTtcblxuLyoqIE9uZSBlbnRyeSBpbiB0aGUgaW5kZXggY2F0YWxvZ3VlICh7QGxpbmsgc2VhcmNoSW5kZXhJbmZvU2NoZW1hfSkuICovXG5leHBvcnQgdHlwZSBTZWFyY2hJbmRleEluZm8gPSB6LmluZmVyPHR5cGVvZiBzZWFyY2hJbmRleEluZm9TY2hlbWE+O1xuXG4vKiogU2NoZW1hIGZvciB0aGUgcGx1Z2luJ3MgY2xpZW50IGNvbmZpZyBzdXJmYWNlZCB0byBhIFVJLiAqL1xuZXhwb3J0IGNvbnN0IHNlYXJjaENsaWVudENvbmZpZ1NjaGVtYSA9IHoub2JqZWN0KHtcbiAgaW5kZXhlczogei5hcnJheShzZWFyY2hJbmRleEluZm9TY2hlbWEpLmRlc2NyaWJlKFwiSW5kZXhlcyB0aGUgc2VhcmNoIFVJIG1heSBxdWVyeS5cIiksXG4gIGRlZmF1bHRJbmRleDogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiVGhlIGluZGV4IGEgc2VhcmNoIGJveCBzZWxlY3RzIGJ5IGRlZmF1bHQuXCIpLFxuICBwYWdlU2l6ZTogei5udW1iZXIoKS5pbnQoKS5kZXNjcmliZShcIkRlZmF1bHQgbnVtYmVyIG9mIGhpdHMgYSBzZWFyY2ggcmV0dXJucy5cIiksXG4gIGJhc2VQYXRoOiB6XG4gICAgLnN0cmluZygpXG4gICAgLmRlc2NyaWJlKFwiQmFzZSBwYXRoIHRoZSBzZWFyY2ggcm91dGVzIGFyZSBtb3VudGVkIHVuZGVyIChlLmcuIC9hcGkvc2VhcmNoKS5cIiksXG59KTtcblxuLyoqIFRoZSBwbHVnaW4ncyBjbGllbnQgY29uZmlnICh7QGxpbmsgc2VhcmNoQ2xpZW50Q29uZmlnU2NoZW1hfSkuICovXG5leHBvcnQgdHlwZSBTZWFyY2hDbGllbnRDb25maWcgPSB6LmluZmVyPHR5cGVvZiBzZWFyY2hDbGllbnRDb25maWdTY2hlbWE+O1xuXG4vKipcbiAqIFNjaGVtYSBmb3IgY3JlYXRpbmcgYW4gaW5kZXggKHRoZSBgY3JlYXRlX2luZGV4YCB0b29sIGlucHV0IC8gdGhlXG4gKiBgUE9TVCAvYXBpL3NlYXJjaC9pbmRleGAgYm9keSkuIFR3byBzaGFwZXMsIGJvdGggaW5mZXJyaW5nIGFnZ3Jlc3NpdmVseTpcbiAqIHBhc3MgYHNvdXJjZVRhYmxlYCBmb3IgYSBEZWx0YSBTeW5jIGluZGV4IChEYXRhYnJpY2tzIGNvbXB1dGVzIGVtYmVkZGluZ3MgYW5kXG4gKiBrZWVwcyBpdCBzeW5jZWQpIG9yIGBlbWJlZGRpbmdEaW1lbnNpb25gIGZvciBhIGRpcmVjdC1hY2Nlc3MgaW5kZXggeW91IHdyaXRlXG4gKiB2ZWN0b3JzIHRvIHlvdXJzZWxmLlxuICovXG5leHBvcnQgY29uc3QgY3JlYXRlSW5kZXhSZXF1ZXN0U2NoZW1hID0gei5vYmplY3Qoe1xuICBuYW1lOiB6LnN0cmluZygpLmRlc2NyaWJlKFwiVW5pdHkgQ2F0YWxvZyBuYW1lIGZvciB0aGUgbmV3IGluZGV4IChjYXRhbG9nLnNjaGVtYS5pbmRleCkuXCIpLFxuICBzb3VyY2VUYWJsZTogelxuICAgIC5zdHJpbmcoKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFxuICAgICAgXCJEZWx0YSB0YWJsZSAoY2F0YWxvZy5zY2hlbWEudGFibGUpIHRvIGJ1aWxkIGEgc3luY2VkIGluZGV4IGZyb20uIFByb3ZpZGUgdGhpcyBPUiBlbWJlZGRpbmdEaW1lbnNpb24uXCIsXG4gICAgKSxcbiAgcHJpbWFyeUtleTogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiUHJpbWFyeS1rZXkgY29sdW1uLiBEZWZhdWx0cyB0byAnaWQnLlwiKSxcbiAgZW1iZWRkaW5nU291cmNlQ29sdW1uOiB6XG4gICAgLnN0cmluZygpXG4gICAgLm9wdGlvbmFsKClcbiAgICAuZGVzY3JpYmUoXG4gICAgICBcIlRleHQgY29sdW1uIGVtYmVkZGluZ3MgYXJlIGNvbXB1dGVkIGZyb20gKERlbHRhIFN5bmMpLiBEZWZhdWx0cyB0byB0aGUgZmlyc3Qgb2YgdGV4dC9jb250ZW50L2JvZHkuXCIsXG4gICAgKSxcbiAgZW1iZWRkaW5nTW9kZWw6IHpcbiAgICAuc3RyaW5nKClcbiAgICAub3B0aW9uYWwoKVxuICAgIC5kZXNjcmliZShcbiAgICAgIFwiRW1iZWRkaW5nIG1vZGVsIGVuZHBvaW50IChuYW1lIG9yIGxvb3NlIG5hbWUpLiBEZWZhdWx0cyB0byB0aGUgYXBwJ3MgZW1iZWRkaW5nIG1vZGVsLCBvciB0aGUgYmVzdCBlbWJlZGRpbmcgZW5kcG9pbnQgaW4gdGhlIHdvcmtzcGFjZS5cIixcbiAgICApLFxuICBlbmRwb2ludDogelxuICAgIC5zdHJpbmcoKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFxuICAgICAgXCJWZWN0b3IgU2VhcmNoIGVuZHBvaW50IHRvIGhvc3QgdGhlIGluZGV4IG9uLiBEZWZhdWx0cyB0byB0aGUgYXBwJ3MgY29uZmlndXJlZCBlbmRwb2ludC5cIixcbiAgICApLFxuICBlbWJlZGRpbmdEaW1lbnNpb246IHpcbiAgICAubnVtYmVyKClcbiAgICAuaW50KClcbiAgICAucG9zaXRpdmUoKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFwiRm9yIGEgZGlyZWN0LWFjY2VzcyBpbmRleCAobm8gc291cmNlIHRhYmxlKTogdGhlIGVtYmVkZGluZyB2ZWN0b3IgZGltZW5zaW9uLlwiKSxcbiAgcGlwZWxpbmVUeXBlOiB6XG4gICAgLmVudW0oW1wiVFJJR0dFUkVEXCIsIFwiQ09OVElOVU9VU1wiXSlcbiAgICAub3B0aW9uYWwoKVxuICAgIC5kZXNjcmliZShcbiAgICAgIFwiRGVsdGEgU3luYyBtb2RlOiAnVFJJR0dFUkVEJyAoZGVmYXVsdCkgc3luY3Mgb24gZGVtYW5kOyAnQ09OVElOVU9VUycga2VlcHMgdGhlIGluZGV4IGZyZXNoLlwiLFxuICAgICksXG4gIGNvbHVtbnNUb1N5bmM6IHpcbiAgICAuYXJyYXkoei5zdHJpbmcoKSlcbiAgICAub3B0aW9uYWwoKVxuICAgIC5kZXNjcmliZShcIkV4dHJhIGNvbHVtbnMgdG8gc3luYyBhbG9uZ3NpZGUgdGhlIGVtYmVkZGluZyBzb3VyY2UgY29sdW1uIChEZWx0YSBTeW5jKS5cIiksXG59KTtcblxuLyoqIEEgdmFsaWRhdGVkIGNyZWF0ZS1pbmRleCByZXF1ZXN0ICh7QGxpbmsgY3JlYXRlSW5kZXhSZXF1ZXN0U2NoZW1hfSkuICovXG5leHBvcnQgdHlwZSBDcmVhdGVJbmRleFJlcXVlc3QgPSB6LmluZmVyPHR5cGVvZiBjcmVhdGVJbmRleFJlcXVlc3RTY2hlbWE+O1xuXG4vKiogU2NoZW1hIGZvciB0aGUgYHN5bmNfaW5kZXhgIHRvb2wgaW5wdXQgLyB0aGUgYFBPU1QgL2FwaS9zZWFyY2gvaW5kZXgvc3luY2AgYm9keS4gKi9cbmV4cG9ydCBjb25zdCBzeW5jSW5kZXhSZXF1ZXN0U2NoZW1hID0gei5vYmplY3Qoe1xuICBpbmRleDogelxuICAgIC5zdHJpbmcoKVxuICAgIC5kZXNjcmliZShcbiAgICAgIFwiVGhlIGluZGV4IHRvIHN5bmMgKG5hbWUgb3IgY29uZmlndXJlZCBhbGlhcykuIERlZmF1bHRzIHRvIHRoZSBhcHAncyBkZWZhdWx0IGluZGV4IHdoZW4gb21pdHRlZC5cIixcbiAgICApXG4gICAgLm9wdGlvbmFsKCksXG59KTtcblxuLyoqIEEgdmFsaWRhdGVkIHN5bmMtaW5kZXggcmVxdWVzdCAoe0BsaW5rIHN5bmNJbmRleFJlcXVlc3RTY2hlbWF9KS4gKi9cbmV4cG9ydCB0eXBlIFN5bmNJbmRleFJlcXVlc3QgPSB6LmluZmVyPHR5cGVvZiBzeW5jSW5kZXhSZXF1ZXN0U2NoZW1hPjtcblxuLyoqIFNjaGVtYSBmb3IgYSByZXNvbHZlZCBpbmRleCBkZWZpbml0aW9uICh0aGUgY3JlYXRlL2dldCByZXN1bHQpLiAqL1xuZXhwb3J0IGNvbnN0IGluZGV4SW5mb1NjaGVtYSA9IHoub2JqZWN0KHtcbiAgbmFtZTogei5zdHJpbmcoKS5kZXNjcmliZShcIlVuaXR5IENhdGFsb2cgbmFtZSBvZiB0aGUgaW5kZXguXCIpLFxuICBlbmRwb2ludDogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiVGhlIFZlY3RvciBTZWFyY2ggZW5kcG9pbnQgaG9zdGluZyB0aGUgaW5kZXguXCIpLFxuICBwcmltYXJ5S2V5OiB6LnN0cmluZygpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJUaGUgaW5kZXgncyBwcmltYXJ5LWtleSBjb2x1bW4uXCIpLFxuICBjb2x1bW5zOiB6LmFycmF5KHouc3RyaW5nKCkpLmRlc2NyaWJlKFwiQ29sdW1ucyBhdmFpbGFibGUgb24gdGhlIGluZGV4LlwiKSxcbiAgcmVhZHk6IHouYm9vbGVhbigpLmRlc2NyaWJlKFwiVHJ1ZSB3aGVuIHRoZSBpbmRleCBoYXMgZmluaXNoZWQgcHJvdmlzaW9uaW5nIGFuZCBjYW4gYmUgcXVlcmllZC5cIiksXG4gIHJvd0NvdW50OiB6Lm51bWJlcigpLmludCgpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJOdW1iZXIgb2YgaW5kZXhlZCByb3dzLCB3aGVuIGtub3duLlwiKSxcbn0pO1xuXG4vKiogQSByZXNvbHZlZCBpbmRleCBkZWZpbml0aW9uICh7QGxpbmsgaW5kZXhJbmZvU2NoZW1hfSkuICovXG5leHBvcnQgdHlwZSBJbmRleEluZm8gPSB6LmluZmVyPHR5cGVvZiBpbmRleEluZm9TY2hlbWE+O1xuIl19
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/search.ts","../index.ts"],"version":"5.9.3"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dbx-tools/shared-search",
|
|
3
|
+
"repository": {
|
|
4
|
+
"type": "git",
|
|
5
|
+
"url": "git+https://github.com/reggie-db/dbx-tools.git",
|
|
6
|
+
"directory": "packages/shared/search"
|
|
7
|
+
},
|
|
8
|
+
"devDependencies": {
|
|
9
|
+
"@types/node": "^24.6.0",
|
|
10
|
+
"tsx": "^4.23.0",
|
|
11
|
+
"typescript": "^5.9.3"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"zod": "4.3.6",
|
|
15
|
+
"@dbx-tools/shared-core": "0.6.9"
|
|
16
|
+
},
|
|
17
|
+
"main": "./lib/index.js",
|
|
18
|
+
"license": "UNLICENSED",
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"version": "0.6.9",
|
|
23
|
+
"types": "./lib/index.d.ts",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./lib/index.d.ts",
|
|
28
|
+
"default": "./lib/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"index.ts",
|
|
34
|
+
"src",
|
|
35
|
+
"lib"
|
|
36
|
+
],
|
|
37
|
+
"dbxToolsConfig": {
|
|
38
|
+
"tags": [
|
|
39
|
+
"shared"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"pnpm exec projen\".",
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "projen build",
|
|
45
|
+
"compile": "projen compile",
|
|
46
|
+
"default": "projen default",
|
|
47
|
+
"package": "projen package",
|
|
48
|
+
"post-compile": "projen post-compile",
|
|
49
|
+
"pre-compile": "projen pre-compile",
|
|
50
|
+
"test": "projen test",
|
|
51
|
+
"watch": "projen watch",
|
|
52
|
+
"projen": "projen"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/search.ts
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-format contract for the AI Search add-on: the query a caller (a UI
|
|
3
|
+
* search box, a Mastra tool, a universal-search route) sends and the hits it
|
|
4
|
+
* gets back, plus the document shape used to add / update index contents.
|
|
5
|
+
*
|
|
6
|
+
* Pure (zod + inferred types, no Node-only imports) so the server-side client
|
|
7
|
+
* in `@dbx-tools/search`, the Mastra tool, the `POST /api/search` route,
|
|
8
|
+
* and the React search box all validate / type against ONE definition.
|
|
9
|
+
*
|
|
10
|
+
* "AI Search" is Databricks' current name for Vector Search: an index built
|
|
11
|
+
* from a Delta table (or written directly) that answers a natural-language
|
|
12
|
+
* query with the most relevant rows, combining vector similarity with BM25
|
|
13
|
+
* keyword matching (hybrid). This contract is index-agnostic - it names the
|
|
14
|
+
* index by its Unity Catalog path and returns hits as `{ id, score, fields }`
|
|
15
|
+
* so the same shape serves an autocomplete box, a docs lookup, and a
|
|
16
|
+
* federated search across many indexes.
|
|
17
|
+
*
|
|
18
|
+
* Array fields intentionally avoid `.min()` / `.nonempty()`: those emit
|
|
19
|
+
* `minItems` in the JSON schema, which some Model Serving endpoints reject
|
|
20
|
+
* when the schema is forwarded as a tool definition.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* How a query is matched. `hybrid` (the default) fuses vector similarity with
|
|
29
|
+
* BM25 keyword ranking - the right choice for most search boxes and for source
|
|
30
|
+
* data with exact identifiers (SKUs, error codes). `vector` is pure semantic
|
|
31
|
+
* similarity; `keyword` is pure BM25 full-text (Beta on Databricks). The
|
|
32
|
+
* value maps onto the serving API's `query_type` (`ANN` / `HYBRID`).
|
|
33
|
+
*/
|
|
34
|
+
export const searchModeSchema = z
|
|
35
|
+
.enum(["hybrid", "vector", "keyword"])
|
|
36
|
+
.describe(
|
|
37
|
+
"Match strategy: 'hybrid' fuses semantic similarity with keyword ranking (default, best for most searches), 'vector' is pure semantic, 'keyword' is pure full-text.",
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
/** How a query is matched (see {@link searchModeSchema}). */
|
|
41
|
+
export type SearchMode = z.infer<typeof searchModeSchema>;
|
|
42
|
+
|
|
43
|
+
/** Schema for a search request against a single index. */
|
|
44
|
+
export const searchRequestSchema = z.object({
|
|
45
|
+
query: z
|
|
46
|
+
.string()
|
|
47
|
+
.describe(
|
|
48
|
+
"What to search for, as natural language or keywords. Prefix fragments work for autocomplete.",
|
|
49
|
+
),
|
|
50
|
+
index: z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe(
|
|
54
|
+
"Unity Catalog name of the index to search (catalog.schema.index). Defaults to the plugin's configured index. Accepts a short alias when one is configured.",
|
|
55
|
+
),
|
|
56
|
+
limit: z
|
|
57
|
+
.number()
|
|
58
|
+
.int()
|
|
59
|
+
.positive()
|
|
60
|
+
.optional()
|
|
61
|
+
.describe("Maximum number of hits to return. Defaults to the plugin's configured page size."),
|
|
62
|
+
mode: searchModeSchema.optional(),
|
|
63
|
+
columns: z
|
|
64
|
+
.array(z.string())
|
|
65
|
+
.optional()
|
|
66
|
+
.describe(
|
|
67
|
+
"Which document columns to return per hit. Defaults to the plugin's configured columns (or all indexed columns).",
|
|
68
|
+
),
|
|
69
|
+
filter: z
|
|
70
|
+
.record(z.string(), z.unknown())
|
|
71
|
+
.optional()
|
|
72
|
+
.describe(
|
|
73
|
+
'Attribute filters as { column: value } (a value, or an operator map like { ">=": 10 }). Combined with AND. Maps onto the index `filters_json`.',
|
|
74
|
+
),
|
|
75
|
+
scoreThreshold: z
|
|
76
|
+
.number()
|
|
77
|
+
.optional()
|
|
78
|
+
.describe("Drop hits whose relevance score is below this value."),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/** A validated search request ({@link searchRequestSchema}). */
|
|
82
|
+
export type SearchRequest = z.infer<typeof searchRequestSchema>;
|
|
83
|
+
|
|
84
|
+
/** Schema for a single search hit. */
|
|
85
|
+
export const searchHitSchema = z.object({
|
|
86
|
+
id: z.string().describe("The document's primary-key value."),
|
|
87
|
+
score: z.number().describe("Relevance score; higher is more relevant."),
|
|
88
|
+
fields: z
|
|
89
|
+
.record(z.string(), z.unknown())
|
|
90
|
+
.describe("The returned columns for this document, keyed by column name."),
|
|
91
|
+
index: z
|
|
92
|
+
.string()
|
|
93
|
+
.optional()
|
|
94
|
+
.describe("Which index the hit came from (set on universal/federated results)."),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/** A single search hit ({@link searchHitSchema}). */
|
|
98
|
+
export type SearchHit = z.infer<typeof searchHitSchema>;
|
|
99
|
+
|
|
100
|
+
/** Schema for the result of a search. */
|
|
101
|
+
export const searchResultSchema = z.object({
|
|
102
|
+
query: z.string().describe("Echo of the query that was searched."),
|
|
103
|
+
hits: z.array(searchHitSchema).describe("Matching documents, most relevant first."),
|
|
104
|
+
index: z.string().optional().describe("The index searched (omitted for a universal search)."),
|
|
105
|
+
count: z.number().int().describe("Number of hits returned."),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/** The outcome of a search ({@link searchResultSchema}). */
|
|
109
|
+
export type SearchResult = z.infer<typeof searchResultSchema>;
|
|
110
|
+
|
|
111
|
+
/** Schema for a universal (federated) search across several indexes at once. */
|
|
112
|
+
export const universalSearchRequestSchema = z.object({
|
|
113
|
+
query: z.string().describe("What to search for across every configured index."),
|
|
114
|
+
indexes: z
|
|
115
|
+
.array(z.string())
|
|
116
|
+
.optional()
|
|
117
|
+
.describe("Which indexes to search. Defaults to every index the plugin knows about."),
|
|
118
|
+
limit: z
|
|
119
|
+
.number()
|
|
120
|
+
.int()
|
|
121
|
+
.positive()
|
|
122
|
+
.optional()
|
|
123
|
+
.describe("Maximum hits PER index before results are merged and re-ranked."),
|
|
124
|
+
mode: searchModeSchema.optional(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
/** A validated universal-search request ({@link universalSearchRequestSchema}). */
|
|
128
|
+
export type UniversalSearchRequest = z.infer<typeof universalSearchRequestSchema>;
|
|
129
|
+
|
|
130
|
+
/** Schema for a document written to a direct-access index. */
|
|
131
|
+
export const searchDocumentSchema = z
|
|
132
|
+
.record(z.string(), z.unknown())
|
|
133
|
+
.describe("A document as { column: value }. Must include the index primary-key column.");
|
|
134
|
+
|
|
135
|
+
/** A document written to an index ({@link searchDocumentSchema}). */
|
|
136
|
+
export type SearchDocument = z.infer<typeof searchDocumentSchema>;
|
|
137
|
+
|
|
138
|
+
/** Schema for the result of adding / updating documents. */
|
|
139
|
+
export const upsertResultSchema = z.object({
|
|
140
|
+
index: z.string().describe("The index the documents were written to."),
|
|
141
|
+
count: z.number().int().describe("Number of documents accepted."),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/** The outcome of an add-documents call ({@link upsertResultSchema}). */
|
|
145
|
+
export type UpsertResult = z.infer<typeof upsertResultSchema>;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Schema for the boot-time index catalogue a UI reads to populate a search box
|
|
149
|
+
* (the payload of the plugin's client config / `GET /api/search/indexes`). An
|
|
150
|
+
* entry names an index, an optional short alias, and the columns worth
|
|
151
|
+
* showing, so a search box needs no server round-trip to render.
|
|
152
|
+
*/
|
|
153
|
+
export const searchIndexInfoSchema = z.object({
|
|
154
|
+
name: z.string().describe("Unity Catalog name of the index (catalog.schema.index)."),
|
|
155
|
+
alias: z.string().optional().describe("Short alias the plugin accepts in place of `name`."),
|
|
156
|
+
primaryKey: z.string().optional().describe("The index's primary-key column, when known."),
|
|
157
|
+
columns: z
|
|
158
|
+
.array(z.string())
|
|
159
|
+
.optional()
|
|
160
|
+
.describe("Columns returned by default, in display order."),
|
|
161
|
+
isDefault: z.boolean().optional().describe("True for the plugin's default index."),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
/** One entry in the index catalogue ({@link searchIndexInfoSchema}). */
|
|
165
|
+
export type SearchIndexInfo = z.infer<typeof searchIndexInfoSchema>;
|
|
166
|
+
|
|
167
|
+
/** Schema for the plugin's client config surfaced to a UI. */
|
|
168
|
+
export const searchClientConfigSchema = z.object({
|
|
169
|
+
indexes: z.array(searchIndexInfoSchema).describe("Indexes the search UI may query."),
|
|
170
|
+
defaultIndex: z.string().optional().describe("The index a search box selects by default."),
|
|
171
|
+
pageSize: z.number().int().describe("Default number of hits a search returns."),
|
|
172
|
+
basePath: z
|
|
173
|
+
.string()
|
|
174
|
+
.describe("Base path the search routes are mounted under (e.g. /api/search)."),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/** The plugin's client config ({@link searchClientConfigSchema}). */
|
|
178
|
+
export type SearchClientConfig = z.infer<typeof searchClientConfigSchema>;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Schema for creating an index (the `create_index` tool input / the
|
|
182
|
+
* `POST /api/search/index` body). Two shapes, both inferring aggressively:
|
|
183
|
+
* pass `sourceTable` for a Delta Sync index (Databricks computes embeddings and
|
|
184
|
+
* keeps it synced) or `embeddingDimension` for a direct-access index you write
|
|
185
|
+
* vectors to yourself.
|
|
186
|
+
*/
|
|
187
|
+
export const createIndexRequestSchema = z.object({
|
|
188
|
+
name: z.string().describe("Unity Catalog name for the new index (catalog.schema.index)."),
|
|
189
|
+
sourceTable: z
|
|
190
|
+
.string()
|
|
191
|
+
.optional()
|
|
192
|
+
.describe(
|
|
193
|
+
"Delta table (catalog.schema.table) to build a synced index from. Provide this OR embeddingDimension.",
|
|
194
|
+
),
|
|
195
|
+
primaryKey: z.string().optional().describe("Primary-key column. Defaults to 'id'."),
|
|
196
|
+
embeddingSourceColumn: z
|
|
197
|
+
.string()
|
|
198
|
+
.optional()
|
|
199
|
+
.describe(
|
|
200
|
+
"Text column embeddings are computed from (Delta Sync). Defaults to the first of text/content/body.",
|
|
201
|
+
),
|
|
202
|
+
embeddingModel: z
|
|
203
|
+
.string()
|
|
204
|
+
.optional()
|
|
205
|
+
.describe(
|
|
206
|
+
"Embedding model endpoint (name or loose name). Defaults to the app's embedding model, or the best embedding endpoint in the workspace.",
|
|
207
|
+
),
|
|
208
|
+
endpoint: z
|
|
209
|
+
.string()
|
|
210
|
+
.optional()
|
|
211
|
+
.describe(
|
|
212
|
+
"Vector Search endpoint to host the index on. Defaults to the app's configured endpoint.",
|
|
213
|
+
),
|
|
214
|
+
embeddingDimension: z
|
|
215
|
+
.number()
|
|
216
|
+
.int()
|
|
217
|
+
.positive()
|
|
218
|
+
.optional()
|
|
219
|
+
.describe("For a direct-access index (no source table): the embedding vector dimension."),
|
|
220
|
+
pipelineType: z
|
|
221
|
+
.enum(["TRIGGERED", "CONTINUOUS"])
|
|
222
|
+
.optional()
|
|
223
|
+
.describe(
|
|
224
|
+
"Delta Sync mode: 'TRIGGERED' (default) syncs on demand; 'CONTINUOUS' keeps the index fresh.",
|
|
225
|
+
),
|
|
226
|
+
columnsToSync: z
|
|
227
|
+
.array(z.string())
|
|
228
|
+
.optional()
|
|
229
|
+
.describe("Extra columns to sync alongside the embedding source column (Delta Sync)."),
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
/** A validated create-index request ({@link createIndexRequestSchema}). */
|
|
233
|
+
export type CreateIndexRequest = z.infer<typeof createIndexRequestSchema>;
|
|
234
|
+
|
|
235
|
+
/** Schema for the `sync_index` tool input / the `POST /api/search/index/sync` body. */
|
|
236
|
+
export const syncIndexRequestSchema = z.object({
|
|
237
|
+
index: z
|
|
238
|
+
.string()
|
|
239
|
+
.describe(
|
|
240
|
+
"The index to sync (name or configured alias). Defaults to the app's default index when omitted.",
|
|
241
|
+
)
|
|
242
|
+
.optional(),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
/** A validated sync-index request ({@link syncIndexRequestSchema}). */
|
|
246
|
+
export type SyncIndexRequest = z.infer<typeof syncIndexRequestSchema>;
|
|
247
|
+
|
|
248
|
+
/** Schema for a resolved index definition (the create/get result). */
|
|
249
|
+
export const indexInfoSchema = z.object({
|
|
250
|
+
name: z.string().describe("Unity Catalog name of the index."),
|
|
251
|
+
endpoint: z.string().optional().describe("The Vector Search endpoint hosting the index."),
|
|
252
|
+
primaryKey: z.string().optional().describe("The index's primary-key column."),
|
|
253
|
+
columns: z.array(z.string()).describe("Columns available on the index."),
|
|
254
|
+
ready: z.boolean().describe("True when the index has finished provisioning and can be queried."),
|
|
255
|
+
rowCount: z.number().int().optional().describe("Number of indexed rows, when known."),
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
/** A resolved index definition ({@link indexInfoSchema}). */
|
|
259
|
+
export type IndexInfo = z.infer<typeof indexInfoSchema>;
|