@duckcodeailabs/dql-agent 1.9.5 → 1.10.0
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/dist/agent-run-engine.d.ts +13 -0
- package/dist/agent-run-engine.d.ts.map +1 -1
- package/dist/agent-run-engine.js +59 -1
- package/dist/agent-run-engine.js.map +1 -1
- package/dist/answer-loop.d.ts +34 -1
- package/dist/answer-loop.d.ts.map +1 -1
- package/dist/answer-loop.js +295 -35
- package/dist/answer-loop.js.map +1 -1
- package/dist/embeddings/provider.d.ts +1 -1
- package/dist/embeddings/provider.d.ts.map +1 -1
- package/dist/embeddings/provider.js.map +1 -1
- package/dist/governed-relational-compiler.d.ts +138 -0
- package/dist/governed-relational-compiler.d.ts.map +1 -0
- package/dist/governed-relational-compiler.js +315 -0
- package/dist/governed-relational-compiler.js.map +1 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/intent-controller.d.ts +5 -0
- package/dist/intent-controller.d.ts.map +1 -1
- package/dist/intent-controller.js.map +1 -1
- package/dist/kg/build.d.ts.map +1 -1
- package/dist/kg/build.js +81 -1
- package/dist/kg/build.js.map +1 -1
- package/dist/meaning-resolution.d.ts +4 -0
- package/dist/meaning-resolution.d.ts.map +1 -1
- package/dist/meaning-resolution.js +14 -4
- package/dist/meaning-resolution.js.map +1 -1
- package/dist/metadata/analysis-planner.d.ts.map +1 -1
- package/dist/metadata/analysis-planner.js +34 -6
- package/dist/metadata/analysis-planner.js.map +1 -1
- package/dist/metadata/catalog.d.ts +83 -2
- package/dist/metadata/catalog.d.ts.map +1 -1
- package/dist/metadata/catalog.js +381 -28
- package/dist/metadata/catalog.js.map +1 -1
- package/dist/metadata/meaning-evidence.d.ts +10 -1
- package/dist/metadata/meaning-evidence.d.ts.map +1 -1
- package/dist/metadata/meaning-evidence.js +78 -1
- package/dist/metadata/meaning-evidence.js.map +1 -1
- package/dist/plan-execution-adapter.d.ts +60 -0
- package/dist/plan-execution-adapter.d.ts.map +1 -0
- package/dist/plan-execution-adapter.js +239 -0
- package/dist/plan-execution-adapter.js.map +1 -0
- package/dist/research-governance.d.ts +56 -0
- package/dist/research-governance.d.ts.map +1 -0
- package/dist/research-governance.js +68 -0
- package/dist/research-governance.js.map +1 -0
- package/dist/research-loop.d.ts +14 -0
- package/dist/research-loop.d.ts.map +1 -1
- package/dist/research-loop.js +77 -23
- package/dist/research-loop.js.map +1 -1
- package/dist/resolved-analytical-plan.d.ts +112 -0
- package/dist/resolved-analytical-plan.d.ts.map +1 -0
- package/dist/resolved-analytical-plan.js +346 -0
- package/dist/resolved-analytical-plan.js.map +1 -0
- package/dist/router.d.ts +3 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +34 -10
- package/dist/router.js.map +1 -1
- package/package.json +4 -4
package/dist/metadata/catalog.js
CHANGED
|
@@ -13,6 +13,7 @@ import { dirname, join } from 'node:path';
|
|
|
13
13
|
import { brotliCompressSync, brotliDecompressSync, constants as zlibConstants } from 'node:zlib';
|
|
14
14
|
import { buildManifest, buildManifestKnowledgeGraph, loadDomainPackageRegistry, loadManifestKnowledgeSkills, loadProjectConfig, parseContractRef, resolveDataLexManifestPath, resolveDbtManifestPath, resolveSemanticLayerWithDiagnostics, extractColumnLineage, normalizeDqlArtifactReference, composeEffectiveTrust, } from '@duckcodeailabs/dql-core';
|
|
15
15
|
import { buildKGFromManifest, buildKGFromSemanticLayer } from '../kg/build.js';
|
|
16
|
+
import { domainContextSearchDomains } from '../domain-context.js';
|
|
16
17
|
import { buildBlockBusinessFingerprint, buildBlockSqlFingerprints } from './block-fingerprints.js';
|
|
17
18
|
import { buildAnalysisQuestionPlan, certifiedApplicabilityForObject, scoreAllowedSqlRelationWithAnalysisPlan, scoreMetadataObjectWithAnalysisPlan, sortAllowedSqlContextForAnalysisPlan, } from './analysis-planner.js';
|
|
18
19
|
import { extractSimpleSelectShape, sourceSqlShapeColumns } from './sql-shape.js';
|
|
@@ -20,11 +21,11 @@ import { grainMatches, requestedGrainFromPlan, } from './grain-gate.js';
|
|
|
20
21
|
import { certifiedFitAllowsTier1, certifiedTerminationVerdict, evaluateCertifiedBlockFit, } from './block-fit.js';
|
|
21
22
|
import { retrieveScopedHints } from '../hints/retrieval.js';
|
|
22
23
|
import { buildFtsMatch } from '../memory/fts-query.js';
|
|
23
|
-
import { envEmbeddingProvider } from '../embeddings/provider.js';
|
|
24
|
+
import { cosineSimilarity, envEmbeddingProvider, HashedTokenEmbeddingProvider, } from '../embeddings/provider.js';
|
|
24
25
|
import { matchExampleParaphrase } from './example-match.js';
|
|
25
26
|
import { loadSkills, selectRelevantSkills } from '../skills/loader.js';
|
|
26
27
|
import { buildMeaningEvidencePackage, } from './meaning-evidence.js';
|
|
27
|
-
export { toAgentRetrievalEvidence } from './meaning-evidence.js';
|
|
28
|
+
export { applyContextPackCompatibility, toAgentRetrievalEvidence } from './meaning-evidence.js';
|
|
28
29
|
const require = createRequire(import.meta.url);
|
|
29
30
|
let databaseCtor = null;
|
|
30
31
|
function loadDatabase() {
|
|
@@ -93,26 +94,36 @@ const OBJECT_PRIORITY = {
|
|
|
93
94
|
};
|
|
94
95
|
const COLUMN_OBJECT_TYPES = new Set(['dbt_column', 'warehouse_column', 'runtime_column', 'runtime_value']);
|
|
95
96
|
// v3 removes the legacy persisted plaintext runtime-value cache (SEC-003).
|
|
96
|
-
const METADATA_INDEX_VERSION = 'metadata-index-
|
|
97
|
+
const METADATA_INDEX_VERSION = 'metadata-index-v5-qualified-vector-lanes';
|
|
98
|
+
const DEFAULT_VECTOR_PROVIDER = new HashedTokenEmbeddingProvider();
|
|
97
99
|
export function defaultMetadataPath(projectRoot) {
|
|
98
100
|
return join(projectRoot, '.dql', 'cache', 'metadata.sqlite');
|
|
99
101
|
}
|
|
100
102
|
export function metadataSnapshotPath(projectRoot, fingerprint) {
|
|
101
|
-
return join(projectRoot, '.dql', 'cache', 'snapshots', `${fingerprint}.sqlite`);
|
|
103
|
+
return join(projectRoot, '.dql', 'cache', 'snapshots', `${METADATA_INDEX_VERSION}-${fingerprint}.sqlite`);
|
|
102
104
|
}
|
|
103
|
-
|
|
105
|
+
function readActiveMetadataSnapshotPointer(projectRoot) {
|
|
104
106
|
const pointerPath = join(projectRoot, '.dql', 'cache', 'active-snapshot.json');
|
|
105
107
|
if (!existsSync(pointerPath))
|
|
106
108
|
return null;
|
|
107
109
|
try {
|
|
108
110
|
const parsed = JSON.parse(readFileSync(pointerPath, 'utf8'));
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
+
const snapshotId = typeof parsed.snapshotId === 'string' ? parsed.snapshotId : '';
|
|
112
|
+
const snapshotPath = typeof parsed.snapshotPath === 'string' ? parsed.snapshotPath : '';
|
|
113
|
+
const indexSchemaVersion = typeof parsed.indexSchemaVersion === 'string'
|
|
114
|
+
? parsed.indexSchemaVersion
|
|
115
|
+
: undefined;
|
|
116
|
+
if (!snapshotId || !snapshotPath || !existsSync(snapshotPath))
|
|
117
|
+
return null;
|
|
118
|
+
return { snapshotId, snapshotPath, indexSchemaVersion };
|
|
111
119
|
}
|
|
112
120
|
catch {
|
|
113
121
|
return null;
|
|
114
122
|
}
|
|
115
123
|
}
|
|
124
|
+
export function activeMetadataSnapshotPath(projectRoot) {
|
|
125
|
+
return readActiveMetadataSnapshotPointer(projectRoot)?.snapshotPath ?? null;
|
|
126
|
+
}
|
|
116
127
|
/** Read the prepared catalog identity without rebuilding the metadata snapshot. */
|
|
117
128
|
export function currentMetadataFingerprint(projectRoot) {
|
|
118
129
|
if (!existsSync(defaultMetadataPath(projectRoot)))
|
|
@@ -150,6 +161,47 @@ export function openActiveKnowledgeSnapshot(projectRoot) {
|
|
|
150
161
|
const path = activeMetadataSnapshotPath(projectRoot) ?? defaultMetadataPath(projectRoot);
|
|
151
162
|
return new MetadataCatalog(path, { readOnly: path !== defaultMetadataPath(projectRoot) });
|
|
152
163
|
}
|
|
164
|
+
const activeKnowledgeSnapshotLeases = new Map();
|
|
165
|
+
/**
|
|
166
|
+
* Acquire one exact immutable SQLite file for a request lifetime. The active
|
|
167
|
+
* pointer is read once; later activations cannot redirect this lease to a
|
|
168
|
+
* different catalog. Callers must release in `finally`.
|
|
169
|
+
*
|
|
170
|
+
* Acceptance: CTX-002, CTX-005.
|
|
171
|
+
*/
|
|
172
|
+
export function acquireActiveKnowledgeSnapshot(projectRoot) {
|
|
173
|
+
const pointer = readActiveMetadataSnapshotPointer(projectRoot);
|
|
174
|
+
const path = pointer?.snapshotPath ?? defaultMetadataPath(projectRoot);
|
|
175
|
+
const catalog = new MetadataCatalog(path, { readOnly: Boolean(pointer) });
|
|
176
|
+
const catalogFingerprint = catalog.state('fingerprint') ?? 'metadata-unavailable';
|
|
177
|
+
if (pointer && pointer.snapshotId !== catalogFingerprint) {
|
|
178
|
+
catalog.close();
|
|
179
|
+
throw new Error(`Active metadata snapshot pointer mismatch (pointer ${pointer.snapshotId}, catalog ${catalogFingerprint}).`);
|
|
180
|
+
}
|
|
181
|
+
activeKnowledgeSnapshotLeases.set(path, (activeKnowledgeSnapshotLeases.get(path) ?? 0) + 1);
|
|
182
|
+
let released = false;
|
|
183
|
+
return Object.freeze({
|
|
184
|
+
snapshotId: catalogFingerprint,
|
|
185
|
+
path,
|
|
186
|
+
catalog,
|
|
187
|
+
release() {
|
|
188
|
+
if (released)
|
|
189
|
+
return;
|
|
190
|
+
released = true;
|
|
191
|
+
catalog.close();
|
|
192
|
+
const remaining = (activeKnowledgeSnapshotLeases.get(path) ?? 1) - 1;
|
|
193
|
+
if (remaining > 0)
|
|
194
|
+
activeKnowledgeSnapshotLeases.set(path, remaining);
|
|
195
|
+
else
|
|
196
|
+
activeKnowledgeSnapshotLeases.delete(path);
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
export function activeKnowledgeSnapshotLeaseCount(path) {
|
|
201
|
+
if (path)
|
|
202
|
+
return activeKnowledgeSnapshotLeases.get(path) ?? 0;
|
|
203
|
+
return Array.from(activeKnowledgeSnapshotLeases.values()).reduce((sum, count) => sum + count, 0);
|
|
204
|
+
}
|
|
153
205
|
export function readIndexedDomainKnowledge(projectRoot, domainId) {
|
|
154
206
|
const catalog = openActiveKnowledgeSnapshot(projectRoot);
|
|
155
207
|
try {
|
|
@@ -295,6 +347,92 @@ export function upsertMetadataSnapshot(projectRoot, snapshot) {
|
|
|
295
347
|
catalog.close();
|
|
296
348
|
}
|
|
297
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Run exact, BM25/lexical, vector, and graph candidate generation as
|
|
352
|
+
* independent lanes over one acquired snapshot. Domain/import eligibility is
|
|
353
|
+
* applied in SQL or before graph admission, never as a post-ranking boost.
|
|
354
|
+
*
|
|
355
|
+
* Acceptance: CTX-005, AGT-009, AGT-010.
|
|
356
|
+
*/
|
|
357
|
+
export async function retrieveMetadataSnapshotCandidates(catalog, input) {
|
|
358
|
+
const limit = Math.max(1, input.limit ?? 80);
|
|
359
|
+
const domains = domainContextSearchDomains(input.domainContext);
|
|
360
|
+
const isEligible = (object) => (object.objectType !== 'skill'
|
|
361
|
+
&& (!input.objectTypes?.length || input.objectTypes.includes(object.objectType))
|
|
362
|
+
&& (domains.length === 0 || !object.domain || domains.includes(object.domain)));
|
|
363
|
+
const explicitIdentities = explicitMetadataIdentities(input.question);
|
|
364
|
+
const exactObjects = explicitIdentities.flatMap((identity) => {
|
|
365
|
+
const resolution = catalog.resolveIdentity(identity);
|
|
366
|
+
return resolution.status === 'resolved' && resolution.object && isEligible(resolution.object)
|
|
367
|
+
? [resolution.object]
|
|
368
|
+
: [];
|
|
369
|
+
});
|
|
370
|
+
const queries = uniqueMetadataSearchQueries(input.searchQueries?.length
|
|
371
|
+
? input.searchQueries
|
|
372
|
+
: [input.question]);
|
|
373
|
+
const lexicalObjects = mergeObjects(queries.flatMap((query) => catalog.searchObjects({
|
|
374
|
+
query,
|
|
375
|
+
objectTypes: input.objectTypes,
|
|
376
|
+
domains: domains.length > 0 ? domains : undefined,
|
|
377
|
+
limit,
|
|
378
|
+
}))).filter(isEligible).slice(0, limit);
|
|
379
|
+
const vector = await catalog.searchVectorObjects({
|
|
380
|
+
query: input.question,
|
|
381
|
+
objectTypes: input.objectTypes,
|
|
382
|
+
domains: domains.length > 0 ? domains : undefined,
|
|
383
|
+
limit: Math.min(limit, 24),
|
|
384
|
+
provider: input.embeddingProvider,
|
|
385
|
+
});
|
|
386
|
+
const vectorObjects = vector.candidates.filter(isEligible);
|
|
387
|
+
const seedKeys = mergeObjects([...exactObjects, ...lexicalObjects.slice(0, 12), ...vectorObjects.slice(0, 12)])
|
|
388
|
+
.map((object) => object.objectKey);
|
|
389
|
+
const graphEdges = catalog.edgesForKeys(seedKeys, 1);
|
|
390
|
+
const graphObjects = catalog.getObjectsByKeys(Array.from(new Set(graphEdges.flatMap((edge) => [edge.fromKey, edge.toKey])))).filter((object) => isEligible(object) && !seedKeys.includes(object.objectKey)).slice(0, Math.min(limit, 24));
|
|
391
|
+
const lanes = [
|
|
392
|
+
retrievalLane('exact', exactObjects, (object) => explicitIdentities.includes(object.objectKey)
|
|
393
|
+
? 'exact object-key reference'
|
|
394
|
+
: 'resolved qualified/native/alias reference'),
|
|
395
|
+
retrievalLane('lexical', lexicalObjects, () => 'BM25/lexical candidate from the immutable snapshot'),
|
|
396
|
+
{
|
|
397
|
+
...retrievalLane('vector', vectorObjects, () => `independent vector candidate from ${vector.providerId}`),
|
|
398
|
+
provider: vector.providerId,
|
|
399
|
+
unavailableReason: vector.unavailableReason,
|
|
400
|
+
},
|
|
401
|
+
retrievalLane('graph', graphObjects, () => 'one-hop neighbor of an eligible exact/lexical/vector seed'),
|
|
402
|
+
];
|
|
403
|
+
const selected = mergeObjects([...exactObjects, ...lexicalObjects, ...vectorObjects, ...graphObjects])
|
|
404
|
+
.slice(0, limit * 2);
|
|
405
|
+
return {
|
|
406
|
+
snapshotId: catalog.state('fingerprint') ?? 'metadata-unavailable',
|
|
407
|
+
selected,
|
|
408
|
+
lanes,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
function retrievalLane(lane, objects, reason) {
|
|
412
|
+
return {
|
|
413
|
+
lane,
|
|
414
|
+
candidates: objects.map((object, index) => ({
|
|
415
|
+
objectKey: object.objectKey,
|
|
416
|
+
objectType: object.objectType,
|
|
417
|
+
name: object.name,
|
|
418
|
+
rank: index + 1,
|
|
419
|
+
score: Number((object.score ?? (1 / (index + 1))).toFixed(6)),
|
|
420
|
+
reason: reason(object),
|
|
421
|
+
})),
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function explicitMetadataIdentities(question) {
|
|
425
|
+
const identities = [];
|
|
426
|
+
for (const match of question.matchAll(/@(metric|dimension|measure|entity|model|block|term)\(([^)]+)\)/gi)) {
|
|
427
|
+
const identity = match[2]?.trim();
|
|
428
|
+
if (identity)
|
|
429
|
+
identities.push(identity);
|
|
430
|
+
}
|
|
431
|
+
for (const match of question.matchAll(/\b(?:semantic|dql|dbt|skill):[a-z0-9_.:-]+/gi)) {
|
|
432
|
+
identities.push(match[0]);
|
|
433
|
+
}
|
|
434
|
+
return Array.from(new Set(identities));
|
|
435
|
+
}
|
|
298
436
|
export async function buildLocalContextPack(projectRoot, request) {
|
|
299
437
|
let prepared = false;
|
|
300
438
|
if (request.preparedMetadataFingerprint) {
|
|
@@ -311,8 +449,9 @@ export async function buildLocalContextPack(projectRoot, request) {
|
|
|
311
449
|
// Static governed knowledge is read from the immutable content-addressed
|
|
312
450
|
// snapshot selected at request start. Mutable run history, runtime schema,
|
|
313
451
|
// and context packs stay in the working catalog and never alter that view.
|
|
314
|
-
const
|
|
315
|
-
const
|
|
452
|
+
const snapshotLease = acquireActiveKnowledgeSnapshot(projectRoot);
|
|
453
|
+
const snapshotPath = snapshotLease.path;
|
|
454
|
+
const catalog = snapshotLease.catalog;
|
|
316
455
|
const runtimeCatalog = openMetadataCatalog(projectRoot);
|
|
317
456
|
try {
|
|
318
457
|
const mode = request.mode ?? 'question';
|
|
@@ -400,11 +539,14 @@ export async function buildLocalContextPack(projectRoot, request) {
|
|
|
400
539
|
// Leaving it in generic FTS results would bypass status/domain/area/exclusion
|
|
401
540
|
// eligibility simply because a word in its body matched the question.
|
|
402
541
|
const retrievalObjects = (rows) => scopeObjects(rows).filter((row) => row.objectType !== 'skill');
|
|
403
|
-
const
|
|
404
|
-
|
|
542
|
+
const snapshotRetrieval = await retrieveMetadataSnapshotCandidates(catalog, {
|
|
543
|
+
question: request.question,
|
|
544
|
+
searchQueries,
|
|
405
545
|
objectTypes: request.objectTypes,
|
|
546
|
+
domainContext: effectiveDomainContext,
|
|
406
547
|
limit: Math.max(request.limit ?? 80, 20),
|
|
407
|
-
})
|
|
548
|
+
});
|
|
549
|
+
const searchRows = retrievalObjects(snapshotRetrieval.selected);
|
|
408
550
|
const schemaShapeCandidates = schemaShapeCandidateObjects(catalog, questionPlan, request, mergeObjects([...runtimeObjects, ...runtimeValueObjects]));
|
|
409
551
|
const schemaShapeObjects = schemaShapeCandidates.map((candidate) => candidate.object);
|
|
410
552
|
const exactCandidate = request.focusObjectKey ? catalog.getObject(request.focusObjectKey) : null;
|
|
@@ -550,6 +692,7 @@ export async function buildLocalContextPack(projectRoot, request) {
|
|
|
550
692
|
hintConflicts: hintResult.conflicts,
|
|
551
693
|
retrievalDiagnostics: {
|
|
552
694
|
strategy: usedFullCatalog ? 'full_catalog' : 'sqlite_fts',
|
|
695
|
+
lanes: snapshotRetrieval.lanes,
|
|
553
696
|
focusedModelAreaId: focusedArea?.id,
|
|
554
697
|
modelAreaSource: focusedArea?.source,
|
|
555
698
|
selectedObjects: objects.length,
|
|
@@ -588,7 +731,7 @@ export async function buildLocalContextPack(projectRoot, request) {
|
|
|
588
731
|
return { ...payload, id };
|
|
589
732
|
}
|
|
590
733
|
finally {
|
|
591
|
-
|
|
734
|
+
snapshotLease.release();
|
|
592
735
|
runtimeCatalog.close();
|
|
593
736
|
}
|
|
594
737
|
}
|
|
@@ -611,7 +754,10 @@ function buildKnowledgeLens(catalog, context, selectedSkills) {
|
|
|
611
754
|
modelAreaId: context?.modelAreaId,
|
|
612
755
|
purpose: context?.purpose,
|
|
613
756
|
skillRefs,
|
|
614
|
-
|
|
757
|
+
// The immutable search snapshot is authoritative. A caller-provided
|
|
758
|
+
// envelope may have been resolved against an earlier manifest signature;
|
|
759
|
+
// it cannot relabel the catalog that actually supplied evidence.
|
|
760
|
+
snapshotId: catalog.state('fingerprint') ?? 'metadata-unavailable',
|
|
615
761
|
capsuleFingerprint: stringValue(capsule?.payload?.fingerprint)
|
|
616
762
|
?? stringValue(capsule?.payload?.sourceFingerprint),
|
|
617
763
|
skillFingerprints: Object.keys(skillFingerprints).length > 0 ? skillFingerprints : undefined,
|
|
@@ -1036,6 +1182,15 @@ export class MetadataCatalog {
|
|
|
1036
1182
|
tokenize = 'porter unicode61'
|
|
1037
1183
|
);
|
|
1038
1184
|
|
|
1185
|
+
CREATE TABLE IF NOT EXISTS metadata_vector_index (
|
|
1186
|
+
object_key TEXT PRIMARY KEY,
|
|
1187
|
+
provider_id TEXT NOT NULL,
|
|
1188
|
+
dimensions INTEGER NOT NULL,
|
|
1189
|
+
vector BLOB NOT NULL,
|
|
1190
|
+
text_hash TEXT NOT NULL
|
|
1191
|
+
);
|
|
1192
|
+
CREATE INDEX IF NOT EXISTS idx_metadata_vector_provider ON metadata_vector_index(provider_id);
|
|
1193
|
+
|
|
1039
1194
|
CREATE TABLE IF NOT EXISTS metadata_edges (
|
|
1040
1195
|
edge_type TEXT NOT NULL,
|
|
1041
1196
|
from_key TEXT NOT NULL,
|
|
@@ -1192,6 +1347,10 @@ export class MetadataCatalog {
|
|
|
1192
1347
|
const insertFts = this.db.prepare(`
|
|
1193
1348
|
INSERT INTO metadata_fts (object_key, name, full_name, description, domain, owner, payload)
|
|
1194
1349
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1350
|
+
`);
|
|
1351
|
+
const insertVector = this.db.prepare(`
|
|
1352
|
+
INSERT INTO metadata_vector_index (object_key, provider_id, dimensions, vector, text_hash)
|
|
1353
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1195
1354
|
`);
|
|
1196
1355
|
const insertEdge = this.db.prepare(`
|
|
1197
1356
|
INSERT OR IGNORE INTO metadata_edges (
|
|
@@ -1226,6 +1385,7 @@ export class MetadataCatalog {
|
|
|
1226
1385
|
const txn = this.db.transaction(() => {
|
|
1227
1386
|
this.db.prepare('DELETE FROM metadata_edges').run();
|
|
1228
1387
|
this.db.prepare('DELETE FROM metadata_fts').run();
|
|
1388
|
+
this.db.prepare('DELETE FROM metadata_vector_index').run();
|
|
1229
1389
|
this.db.prepare('DELETE FROM metadata_objects').run();
|
|
1230
1390
|
this.db.prepare('DELETE FROM metadata_diagnostics').run();
|
|
1231
1391
|
this.db.prepare('DELETE FROM metadata_source_fingerprints').run();
|
|
@@ -1235,6 +1395,11 @@ export class MetadataCatalog {
|
|
|
1235
1395
|
const payload = object.payload ?? {};
|
|
1236
1396
|
insertObject.run(object.objectKey, object.objectType, object.name, object.fullName ?? null, object.domain ?? null, object.owner ?? null, object.status ?? null, object.description ?? null, object.sourcePath ?? null, object.sourceSystem ?? null, JSON.stringify(payload), object.updatedAt ?? now);
|
|
1237
1397
|
insertFts.run(object.objectKey, object.name, object.fullName ?? '', object.description ?? '', object.domain ?? '', object.owner ?? '', searchableMetadataPayload(payload));
|
|
1398
|
+
if (isVectorIndexObject(object)) {
|
|
1399
|
+
const vectorText = metadataVectorText(object);
|
|
1400
|
+
const vector = DEFAULT_VECTOR_PROVIDER.embedOne(vectorText);
|
|
1401
|
+
insertVector.run(object.objectKey, DEFAULT_VECTOR_PROVIDER.id, vector.length, encodeFloat32Vector(vector), sha256(vectorText));
|
|
1402
|
+
}
|
|
1238
1403
|
}
|
|
1239
1404
|
for (const edge of snapshot.edges) {
|
|
1240
1405
|
insertEdge.run(edge.edgeType, edge.fromKey, edge.toKey, edge.confidence ?? 1, JSON.stringify(edge.payload ?? {}), now);
|
|
@@ -1262,6 +1427,8 @@ export class MetadataCatalog {
|
|
|
1262
1427
|
setState.run('compile_conflicts_json', JSON.stringify(snapshot.compileConflicts ?? []));
|
|
1263
1428
|
setState.run('manifest_generated_at', snapshot.manifest.generatedAt);
|
|
1264
1429
|
setState.run('index_version', METADATA_INDEX_VERSION);
|
|
1430
|
+
setState.run('vector_provider', DEFAULT_VECTOR_PROVIDER.id);
|
|
1431
|
+
setState.run('vector_dimensions', String(DEFAULT_VECTOR_PROVIDER.dimensions));
|
|
1265
1432
|
});
|
|
1266
1433
|
txn();
|
|
1267
1434
|
}
|
|
@@ -1303,6 +1470,9 @@ export class MetadataCatalog {
|
|
|
1303
1470
|
const insertFts = this.db.prepare(`
|
|
1304
1471
|
INSERT INTO metadata_fts (object_key, name, full_name, description, domain, owner, payload)
|
|
1305
1472
|
VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
1473
|
+
const insertVector = this.db.prepare(`
|
|
1474
|
+
INSERT INTO metadata_vector_index (object_key, provider_id, dimensions, vector, text_hash)
|
|
1475
|
+
VALUES (?, ?, ?, ?, ?)`);
|
|
1306
1476
|
const insertEdge = this.db.prepare(`
|
|
1307
1477
|
INSERT OR IGNORE INTO metadata_edges (edge_type, from_key, to_key, confidence, payload_json, created_at)
|
|
1308
1478
|
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
@@ -1324,15 +1494,18 @@ export class MetadataCatalog {
|
|
|
1324
1494
|
INSERT OR REPLACE INTO skill_bodies (body_hash, encoding, compressed_body, original_length)
|
|
1325
1495
|
VALUES (?, 'br', ?, ?)`);
|
|
1326
1496
|
const deleteFtsForSource = this.db.prepare('DELETE FROM metadata_fts WHERE object_key IN (SELECT object_key FROM metadata_objects WHERE COALESCE(source_path, source_system) = ?)');
|
|
1497
|
+
const deleteVectorsForSource = this.db.prepare('DELETE FROM metadata_vector_index WHERE object_key IN (SELECT object_key FROM metadata_objects WHERE COALESCE(source_path, source_system) = ?)');
|
|
1327
1498
|
const deleteObjectsForSource = this.db.prepare('DELETE FROM metadata_objects WHERE COALESCE(source_path, source_system) = ?');
|
|
1328
1499
|
const txn = this.db.transaction(() => {
|
|
1329
1500
|
// Drop changed + removed sources' rows, and ALL sourceless rows (never
|
|
1330
1501
|
// fingerprinted, so always refreshed).
|
|
1331
1502
|
for (const source of [...changedSources, ...removedSources]) {
|
|
1332
1503
|
deleteFtsForSource.run(source);
|
|
1504
|
+
deleteVectorsForSource.run(source);
|
|
1333
1505
|
deleteObjectsForSource.run(source);
|
|
1334
1506
|
}
|
|
1335
1507
|
this.db.prepare('DELETE FROM metadata_fts WHERE object_key IN (SELECT object_key FROM metadata_objects WHERE source_path IS NULL AND source_system IS NULL)').run();
|
|
1508
|
+
this.db.prepare('DELETE FROM metadata_vector_index WHERE object_key IN (SELECT object_key FROM metadata_objects WHERE source_path IS NULL AND source_system IS NULL)').run();
|
|
1336
1509
|
this.db.prepare('DELETE FROM metadata_objects WHERE source_path IS NULL AND source_system IS NULL').run();
|
|
1337
1510
|
// Re-insert only objects from changed sources or sourceless; unchanged
|
|
1338
1511
|
// sources' rows are left untouched.
|
|
@@ -1343,6 +1516,11 @@ export class MetadataCatalog {
|
|
|
1343
1516
|
const payload = object.payload ?? {};
|
|
1344
1517
|
insertObject.run(object.objectKey, object.objectType, object.name, object.fullName ?? null, object.domain ?? null, object.owner ?? null, object.status ?? null, object.description ?? null, object.sourcePath ?? null, object.sourceSystem ?? null, JSON.stringify(payload), object.updatedAt ?? now);
|
|
1345
1518
|
insertFts.run(object.objectKey, object.name, object.fullName ?? '', object.description ?? '', object.domain ?? '', object.owner ?? '', searchableMetadataPayload(payload));
|
|
1519
|
+
if (isVectorIndexObject(object)) {
|
|
1520
|
+
const vectorText = metadataVectorText(object);
|
|
1521
|
+
const vector = DEFAULT_VECTOR_PROVIDER.embedOne(vectorText);
|
|
1522
|
+
insertVector.run(object.objectKey, DEFAULT_VECTOR_PROVIDER.id, vector.length, encodeFloat32Vector(vector), sha256(vectorText));
|
|
1523
|
+
}
|
|
1346
1524
|
}
|
|
1347
1525
|
// Cross-source tables: rebuild fully.
|
|
1348
1526
|
this.db.prepare('DELETE FROM metadata_edges').run();
|
|
@@ -1378,12 +1556,14 @@ export class MetadataCatalog {
|
|
|
1378
1556
|
setState.run('compile_conflicts_json', JSON.stringify(snapshot.compileConflicts ?? []));
|
|
1379
1557
|
setState.run('manifest_generated_at', snapshot.manifest.generatedAt);
|
|
1380
1558
|
setState.run('index_version', METADATA_INDEX_VERSION);
|
|
1559
|
+
setState.run('vector_provider', DEFAULT_VECTOR_PROVIDER.id);
|
|
1560
|
+
setState.run('vector_dimensions', String(DEFAULT_VECTOR_PROVIDER.dimensions));
|
|
1381
1561
|
});
|
|
1382
1562
|
txn();
|
|
1383
1563
|
return { mode: 'incremental', changedSources: changedSources.size };
|
|
1384
1564
|
}
|
|
1385
1565
|
searchObjects(options) {
|
|
1386
|
-
const { query, objectTypes, domain, limit = 40 } = options;
|
|
1566
|
+
const { query, objectTypes, domain, domains, limit = 40 } = options;
|
|
1387
1567
|
const match = buildFtsMatch(query, { prefix: true });
|
|
1388
1568
|
if (!match.or)
|
|
1389
1569
|
return this.listObjects({ objectTypes, domain, limit });
|
|
@@ -1397,6 +1577,10 @@ export class MetadataCatalog {
|
|
|
1397
1577
|
filters.push('o.domain = ?');
|
|
1398
1578
|
extraParams.push(domain);
|
|
1399
1579
|
}
|
|
1580
|
+
else if (domains?.length) {
|
|
1581
|
+
filters.push(`(o.domain IS NULL OR o.domain IN (${domains.map(() => '?').join(', ')}))`);
|
|
1582
|
+
extraParams.push(...domains);
|
|
1583
|
+
}
|
|
1400
1584
|
const whereExtra = filters.length > 0 ? ` AND ${filters.join(' AND ')}` : '';
|
|
1401
1585
|
const runMatch = (matchExpr) => this.db.prepare(`
|
|
1402
1586
|
SELECT o.*,
|
|
@@ -1429,6 +1613,106 @@ export class MetadataCatalog {
|
|
|
1429
1613
|
snippet: row.snip ?? undefined,
|
|
1430
1614
|
}));
|
|
1431
1615
|
}
|
|
1616
|
+
/** Replace the snapshot vector lane with an explicitly configured provider. */
|
|
1617
|
+
async rebuildVectorIndex(provider, batchSize = 96) {
|
|
1618
|
+
const objects = [];
|
|
1619
|
+
this.scanObjects({ batchSize: 500 }, (rows) => {
|
|
1620
|
+
objects.push(...rows.filter(isVectorIndexObject));
|
|
1621
|
+
});
|
|
1622
|
+
const encoded = [];
|
|
1623
|
+
let dimensions = 0;
|
|
1624
|
+
for (let offset = 0; offset < objects.length; offset += Math.max(1, batchSize)) {
|
|
1625
|
+
const batch = objects.slice(offset, offset + Math.max(1, batchSize));
|
|
1626
|
+
const texts = batch.map(metadataVectorText);
|
|
1627
|
+
const vectors = await provider.embed(texts);
|
|
1628
|
+
if (vectors.length !== batch.length) {
|
|
1629
|
+
throw new Error(`Embedding provider ${provider.id} returned ${vectors.length} vectors for ${batch.length} objects.`);
|
|
1630
|
+
}
|
|
1631
|
+
for (let index = 0; index < batch.length; index += 1) {
|
|
1632
|
+
const vector = vectors[index] ?? [];
|
|
1633
|
+
if (vector.length === 0)
|
|
1634
|
+
throw new Error(`Embedding provider ${provider.id} returned an empty vector.`);
|
|
1635
|
+
if (dimensions === 0)
|
|
1636
|
+
dimensions = vector.length;
|
|
1637
|
+
if (vector.length !== dimensions) {
|
|
1638
|
+
throw new Error(`Embedding provider ${provider.id} changed dimensions from ${dimensions} to ${vector.length}.`);
|
|
1639
|
+
}
|
|
1640
|
+
encoded.push({
|
|
1641
|
+
objectKey: batch[index].objectKey,
|
|
1642
|
+
dimensions,
|
|
1643
|
+
vector: encodeFloat32Vector(vector),
|
|
1644
|
+
textHash: sha256(texts[index]),
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
const insert = this.db.prepare(`
|
|
1649
|
+
INSERT INTO metadata_vector_index (object_key, provider_id, dimensions, vector, text_hash)
|
|
1650
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1651
|
+
`);
|
|
1652
|
+
const setState = this.db.prepare('INSERT OR REPLACE INTO metadata_state (key, value) VALUES (?, ?)');
|
|
1653
|
+
this.db.transaction(() => {
|
|
1654
|
+
this.db.prepare('DELETE FROM metadata_vector_index').run();
|
|
1655
|
+
for (const item of encoded) {
|
|
1656
|
+
insert.run(item.objectKey, provider.id, item.dimensions, item.vector, item.textHash);
|
|
1657
|
+
}
|
|
1658
|
+
setState.run('vector_provider', provider.id);
|
|
1659
|
+
setState.run('vector_dimensions', String(dimensions));
|
|
1660
|
+
})();
|
|
1661
|
+
}
|
|
1662
|
+
/** Independent vector candidate generation over all eligible snapshot cards. */
|
|
1663
|
+
async searchVectorObjects(options) {
|
|
1664
|
+
const indexedProviderId = this.state('vector_provider') ?? DEFAULT_VECTOR_PROVIDER.id;
|
|
1665
|
+
const indexedDimensions = Number(this.state('vector_dimensions') ?? DEFAULT_VECTOR_PROVIDER.dimensions);
|
|
1666
|
+
const provider = options.provider ?? DEFAULT_VECTOR_PROVIDER;
|
|
1667
|
+
if (provider.id !== indexedProviderId) {
|
|
1668
|
+
return {
|
|
1669
|
+
providerId: indexedProviderId,
|
|
1670
|
+
dimensions: indexedDimensions,
|
|
1671
|
+
candidates: [],
|
|
1672
|
+
unavailableReason: `requested provider ${provider.id} does not match snapshot vector provider ${indexedProviderId}`,
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
const [queryVector] = provider === DEFAULT_VECTOR_PROVIDER
|
|
1676
|
+
? [DEFAULT_VECTOR_PROVIDER.embedOne(options.query)]
|
|
1677
|
+
: await provider.embed([options.query]);
|
|
1678
|
+
if (!queryVector || queryVector.length !== indexedDimensions) {
|
|
1679
|
+
return {
|
|
1680
|
+
providerId: indexedProviderId,
|
|
1681
|
+
dimensions: indexedDimensions,
|
|
1682
|
+
candidates: [],
|
|
1683
|
+
unavailableReason: `query vector dimensions do not match snapshot index (${queryVector?.length ?? 0} vs ${indexedDimensions})`,
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
const filters = ['v.provider_id = ?'];
|
|
1687
|
+
const params = [indexedProviderId];
|
|
1688
|
+
if (options.objectTypes?.length) {
|
|
1689
|
+
filters.push(`o.object_type IN (${options.objectTypes.map(() => '?').join(', ')})`);
|
|
1690
|
+
params.push(...options.objectTypes);
|
|
1691
|
+
}
|
|
1692
|
+
if (options.domains?.length) {
|
|
1693
|
+
filters.push(`(o.domain IS NULL OR o.domain IN (${options.domains.map(() => '?').join(', ')}))`);
|
|
1694
|
+
params.push(...options.domains);
|
|
1695
|
+
}
|
|
1696
|
+
const rows = this.db.prepare(`
|
|
1697
|
+
SELECT o.*, v.vector AS vector_blob
|
|
1698
|
+
FROM metadata_vector_index AS v
|
|
1699
|
+
JOIN metadata_objects AS o ON o.object_key = v.object_key
|
|
1700
|
+
WHERE ${filters.join(' AND ')}
|
|
1701
|
+
ORDER BY o.object_key
|
|
1702
|
+
`).all(...params);
|
|
1703
|
+
const candidates = rows
|
|
1704
|
+
.map((row) => {
|
|
1705
|
+
const cosine = cosineSimilarity(queryVector, decodeFloat32Vector(row.vector_blob));
|
|
1706
|
+
return { ...rowToObject(row), score: Number(((cosine + 1) / 2).toFixed(6)) };
|
|
1707
|
+
})
|
|
1708
|
+
// A cosine of zero maps to 0.5 and is not evidence. Requiring positive
|
|
1709
|
+
// separation prevents hash collisions/unrelated cards from flooding the
|
|
1710
|
+
// downstream bounded context while retaining independent vector recall.
|
|
1711
|
+
.filter((row) => (row.score ?? 0) > 0.55)
|
|
1712
|
+
.sort((left, right) => (right.score ?? 0) - (left.score ?? 0) || left.objectKey.localeCompare(right.objectKey))
|
|
1713
|
+
.slice(0, Math.max(1, options.limit ?? 40));
|
|
1714
|
+
return { providerId: indexedProviderId, dimensions: indexedDimensions, candidates };
|
|
1715
|
+
}
|
|
1432
1716
|
listObjects(options = {}) {
|
|
1433
1717
|
const filters = [];
|
|
1434
1718
|
const params = [];
|
|
@@ -1556,13 +1840,49 @@ export class MetadataCatalog {
|
|
|
1556
1840
|
return rows;
|
|
1557
1841
|
}
|
|
1558
1842
|
findObjectByIdentity(identity) {
|
|
1843
|
+
return this.resolveIdentity(identity).object ?? null;
|
|
1844
|
+
}
|
|
1845
|
+
/** Resolve exact/qualified/native/alias identities without first-match wins. */
|
|
1846
|
+
resolveIdentity(identity) {
|
|
1847
|
+
const normalized = identity.trim();
|
|
1848
|
+
if (!normalized)
|
|
1849
|
+
return { identity, status: 'missing', candidates: [] };
|
|
1850
|
+
const exact = this.db.prepare(`
|
|
1851
|
+
SELECT * FROM metadata_objects WHERE object_key = ?
|
|
1852
|
+
`).get(normalized);
|
|
1853
|
+
if (exact) {
|
|
1854
|
+
const object = rowToObject(exact);
|
|
1855
|
+
return { identity, status: 'resolved', matchedBy: 'object_key', object, candidates: [object] };
|
|
1856
|
+
}
|
|
1559
1857
|
const rows = this.db.prepare(`
|
|
1560
|
-
SELECT
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1858
|
+
SELECT DISTINCT o.*
|
|
1859
|
+
FROM metadata_objects AS o
|
|
1860
|
+
WHERE o.full_name = ?
|
|
1861
|
+
OR json_extract(o.payload_json, '$.sourceNativeId') = ?
|
|
1862
|
+
OR EXISTS (
|
|
1863
|
+
SELECT 1
|
|
1864
|
+
FROM json_each(COALESCE(json_extract(o.payload_json, '$.aliases'), '[]')) AS alias
|
|
1865
|
+
WHERE alias.value = ?
|
|
1866
|
+
)
|
|
1867
|
+
ORDER BY o.object_key
|
|
1868
|
+
LIMIT 21
|
|
1869
|
+
`).all(normalized, normalized, normalized);
|
|
1870
|
+
const candidates = rows.map(rowToObject);
|
|
1871
|
+
if (candidates.length === 0)
|
|
1872
|
+
return { identity, status: 'missing', candidates: [] };
|
|
1873
|
+
if (candidates.length > 1)
|
|
1874
|
+
return { identity, status: 'ambiguous', candidates };
|
|
1875
|
+
const object = candidates[0];
|
|
1876
|
+
const payload = object.payload ?? {};
|
|
1877
|
+
const aliases = metadataStringArray(payload.aliases);
|
|
1878
|
+
const matchedBy = object.fullName === normalized
|
|
1879
|
+
? 'qualified_id'
|
|
1880
|
+
: stringValue(payload.sourceNativeId) === normalized
|
|
1881
|
+
? 'source_native_id'
|
|
1882
|
+
: aliases.includes(normalized)
|
|
1883
|
+
? 'alias'
|
|
1884
|
+
: 'qualified_id';
|
|
1885
|
+
return { identity, status: 'resolved', matchedBy, object, candidates };
|
|
1566
1886
|
}
|
|
1567
1887
|
edgesForKeys(keys, hops = 1) {
|
|
1568
1888
|
let frontier = new Set(keys.filter(Boolean));
|
|
@@ -1944,11 +2264,11 @@ function objectFromKGNode(node) {
|
|
|
1944
2264
|
reviewCadence: node.reviewCadence,
|
|
1945
2265
|
pattern: node.pattern,
|
|
1946
2266
|
grain: node.grain,
|
|
1947
|
-
entities: node.entities ?? [],
|
|
2267
|
+
entities: node.entities ?? node.payload?.entities ?? [],
|
|
1948
2268
|
declaredOutputs: node.declaredOutputs ?? [],
|
|
1949
2269
|
outputs: node.outputs ?? [],
|
|
1950
2270
|
outputContract: node.outputContract ?? [],
|
|
1951
|
-
dimensions: node.dimensions ?? [],
|
|
2271
|
+
dimensions: node.dimensions ?? node.payload?.dimensions ?? [],
|
|
1952
2272
|
allowedFilters: node.allowedFilters ?? [],
|
|
1953
2273
|
parameterPolicy: node.parameterPolicy ?? [],
|
|
1954
2274
|
parameters: node.parameters ?? [],
|
|
@@ -3190,7 +3510,7 @@ async function planContextPackRoute(input) {
|
|
|
3190
3510
|
plan: input.questionPlan,
|
|
3191
3511
|
block: object,
|
|
3192
3512
|
exactExampleMatch: hasExactExampleQuestion(input.request.question, object),
|
|
3193
|
-
definitionLookup: intent === 'definition_lookup',
|
|
3513
|
+
definitionLookup: intent === 'definition_lookup' && objectNameInQuestion(input.request.question, object),
|
|
3194
3514
|
}),
|
|
3195
3515
|
applicabilityScore: applicabilityByKey.get(object.objectKey)?.score ?? 0,
|
|
3196
3516
|
}))
|
|
@@ -3238,7 +3558,7 @@ async function planContextPackRoute(input) {
|
|
|
3238
3558
|
: undefined;
|
|
3239
3559
|
const exactExampleMatch = exact ? hasExactExampleQuestion(input.request.question, exact) : false;
|
|
3240
3560
|
const directCertifiedBypass = Boolean(exact && (exactExampleMatch
|
|
3241
|
-
|| intent === 'definition_lookup'
|
|
3561
|
+
|| (intent === 'definition_lookup' && objectNameInQuestion(input.request.question, exact))
|
|
3242
3562
|
|| objectNameInQuestion(input.request.question, exact)));
|
|
3243
3563
|
const blockFitObject = exact
|
|
3244
3564
|
?? (contextApplicability ? input.objects.find((object) => object.objectKey === contextApplicability.objectKey) : undefined);
|
|
@@ -3248,7 +3568,8 @@ async function planContextPackRoute(input) {
|
|
|
3248
3568
|
plan: input.questionPlan,
|
|
3249
3569
|
block: blockFitObject,
|
|
3250
3570
|
exactExampleMatch: exact ? exactExampleMatch : false,
|
|
3251
|
-
definitionLookup: Boolean(exact && intent === 'definition_lookup'
|
|
3571
|
+
definitionLookup: Boolean(exact && intent === 'definition_lookup'
|
|
3572
|
+
&& objectNameInQuestion(input.request.question, blockFitObject)),
|
|
3252
3573
|
})
|
|
3253
3574
|
: undefined;
|
|
3254
3575
|
const blockFit = rawBlockFit && blockFitObject
|
|
@@ -3635,8 +3956,14 @@ function isGeneratedMetadataIntent(intent) {
|
|
|
3635
3956
|
function findExactCertifiedObject(question, intent, objects) {
|
|
3636
3957
|
const candidates = objects.filter((object) => isCertifiedMetadataObject(object));
|
|
3637
3958
|
if (intent === 'definition_lookup') {
|
|
3638
|
-
|
|
3639
|
-
|
|
3959
|
+
// A definition-shaped sentence is not itself evidence of a match. Small
|
|
3960
|
+
// catalogs often return every certified term; selecting the first one made
|
|
3961
|
+
// unrelated questions (for example weather) look governed. Prefer an
|
|
3962
|
+
// explicitly named/example block, then require the term/view name itself.
|
|
3963
|
+
return candidates.find((object) => object.objectType === 'dql_block'
|
|
3964
|
+
&& (hasExactExampleQuestion(question, object) || objectNameInQuestion(question, object)))
|
|
3965
|
+
?? candidates.find((object) => (object.objectType === 'dql_term' || object.objectType === 'business_view')
|
|
3966
|
+
&& objectNameInQuestion(question, object));
|
|
3640
3967
|
}
|
|
3641
3968
|
const namedExact = candidates.find((object) => object.objectType === 'dql_block' &&
|
|
3642
3969
|
objectNameInQuestion(question, object) &&
|
|
@@ -5555,6 +5882,32 @@ function searchableMetadataPayload(payload) {
|
|
|
5555
5882
|
visit(payload, 0);
|
|
5556
5883
|
return tokens.join(' ');
|
|
5557
5884
|
}
|
|
5885
|
+
const VECTOR_EXCLUDED_OBJECT_TYPES = new Set([
|
|
5886
|
+
...COLUMN_OBJECT_TYPES,
|
|
5887
|
+
'skill',
|
|
5888
|
+
'runtime_value',
|
|
5889
|
+
]);
|
|
5890
|
+
function isVectorIndexObject(object) {
|
|
5891
|
+
return !VECTOR_EXCLUDED_OBJECT_TYPES.has(object.objectType);
|
|
5892
|
+
}
|
|
5893
|
+
function metadataVectorText(object) {
|
|
5894
|
+
return [
|
|
5895
|
+
object.name,
|
|
5896
|
+
object.fullName ?? '',
|
|
5897
|
+
object.description ?? '',
|
|
5898
|
+
object.domain ?? '',
|
|
5899
|
+
object.owner ?? '',
|
|
5900
|
+
searchableMetadataPayload(object.payload ?? {}),
|
|
5901
|
+
].filter(Boolean).join('\n').slice(0, 16_000);
|
|
5902
|
+
}
|
|
5903
|
+
function encodeFloat32Vector(vector) {
|
|
5904
|
+
const values = Float32Array.from(vector);
|
|
5905
|
+
return Buffer.from(values.buffer, values.byteOffset, values.byteLength);
|
|
5906
|
+
}
|
|
5907
|
+
function decodeFloat32Vector(blob) {
|
|
5908
|
+
const bytes = blob.buffer.slice(blob.byteOffset, blob.byteOffset + blob.byteLength);
|
|
5909
|
+
return new Float32Array(bytes);
|
|
5910
|
+
}
|
|
5558
5911
|
function rowToObject(row) {
|
|
5559
5912
|
return {
|
|
5560
5913
|
objectKey: row.object_key,
|