@seanhogg/builderforce-memory 2026.7.14 → 2026.9.27
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 +10 -0
- package/dist/bridges/AnthropicBridge.d.ts +9 -1
- package/dist/bridges/AnthropicBridge.d.ts.map +1 -1
- package/dist/bridges/AnthropicBridge.js +58 -2
- package/dist/bridges/AnthropicBridge.js.map +1 -1
- package/dist/bridges/CachingBridge.d.ts +9 -1
- package/dist/bridges/CachingBridge.d.ts.map +1 -1
- package/dist/bridges/CachingBridge.js +24 -1
- package/dist/bridges/CachingBridge.js.map +1 -1
- package/dist/bridges/OpenAIBridge.d.ts +4 -1
- package/dist/bridges/OpenAIBridge.d.ts.map +1 -1
- package/dist/bridges/OpenAIBridge.js +40 -2
- package/dist/bridges/OpenAIBridge.js.map +1 -1
- package/dist/bridges/SemanticCachingBridge.d.ts +8 -1
- package/dist/bridges/SemanticCachingBridge.d.ts.map +1 -1
- package/dist/bridges/SemanticCachingBridge.js +26 -1
- package/dist/bridges/SemanticCachingBridge.js.map +1 -1
- package/dist/bridges/TransformerBridge.d.ts +13 -0
- package/dist/bridges/TransformerBridge.d.ts.map +1 -1
- package/dist/bridges/VertexAIBridge.d.ts +102 -0
- package/dist/bridges/VertexAIBridge.d.ts.map +1 -0
- package/dist/bridges/VertexAIBridge.js +224 -0
- package/dist/bridges/VertexAIBridge.js.map +1 -0
- package/dist/bridges/index.d.ts +3 -1
- package/dist/bridges/index.d.ts.map +1 -1
- package/dist/bridges/index.js +1 -0
- package/dist/bridges/index.js.map +1 -1
- package/dist/eval/EvalHarness.d.ts +37 -0
- package/dist/eval/EvalHarness.d.ts.map +1 -0
- package/dist/eval/EvalHarness.js +178 -0
- package/dist/eval/EvalHarness.js.map +1 -0
- package/dist/eval/graders.d.ts +76 -0
- package/dist/eval/graders.d.ts.map +1 -0
- package/dist/eval/graders.js +168 -0
- package/dist/eval/graders.js.map +1 -0
- package/dist/eval/index.d.ts +26 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +26 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/types.d.ts +97 -0
- package/dist/eval/types.d.ts.map +1 -0
- package/dist/eval/types.js +5 -0
- package/dist/eval/types.js.map +1 -0
- package/dist/evermind/contract.d.ts +40 -0
- package/dist/evermind/contract.d.ts.map +1 -0
- package/dist/evermind/contract.js +13 -0
- package/dist/evermind/contract.js.map +1 -0
- package/dist/evermind/index.d.ts +15 -0
- package/dist/evermind/index.d.ts.map +1 -0
- package/dist/evermind/index.js +4 -0
- package/dist/evermind/index.js.map +1 -0
- package/dist/evermind/memoryBlock.d.ts +7 -0
- package/dist/evermind/memoryBlock.d.ts.map +1 -0
- package/dist/evermind/memoryBlock.js +37 -0
- package/dist/evermind/memoryBlock.js.map +1 -0
- package/dist/evermind/recall.d.ts +55 -0
- package/dist/evermind/recall.d.ts.map +1 -0
- package/dist/evermind/recall.js +105 -0
- package/dist/evermind/recall.js.map +1 -0
- package/dist/index.d.ts +18 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/ingest/IngestionPipeline.d.ts +95 -0
- package/dist/ingest/IngestionPipeline.d.ts.map +1 -0
- package/dist/ingest/IngestionPipeline.js +295 -0
- package/dist/ingest/IngestionPipeline.js.map +1 -0
- package/dist/ingest/index.d.ts +10 -0
- package/dist/ingest/index.d.ts.map +1 -0
- package/dist/ingest/index.js +8 -0
- package/dist/ingest/index.js.map +1 -0
- package/dist/ingest/parsers.d.ts +53 -0
- package/dist/ingest/parsers.d.ts.map +1 -0
- package/dist/ingest/parsers.js +306 -0
- package/dist/ingest/parsers.js.map +1 -0
- package/dist/ingest/types.d.ts +114 -0
- package/dist/ingest/types.d.ts.map +1 -0
- package/dist/ingest/types.js +19 -0
- package/dist/ingest/types.js.map +1 -0
- package/dist/memory/MemoryStore.d.ts +33 -0
- package/dist/memory/MemoryStore.d.ts.map +1 -1
- package/dist/memory/MemoryStore.js +29 -3
- package/dist/memory/MemoryStore.js.map +1 -1
- package/dist/memory/index.d.ts +1 -1
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/orchestration/AgentGraph.d.ts +104 -0
- package/dist/orchestration/AgentGraph.d.ts.map +1 -0
- package/dist/orchestration/AgentGraph.js +364 -0
- package/dist/orchestration/AgentGraph.js.map +1 -0
- package/dist/orchestration/checkpoint.d.ts +58 -0
- package/dist/orchestration/checkpoint.d.ts.map +1 -0
- package/dist/orchestration/checkpoint.js +92 -0
- package/dist/orchestration/checkpoint.js.map +1 -0
- package/dist/orchestration/index.d.ts +14 -0
- package/dist/orchestration/index.d.ts.map +1 -0
- package/dist/orchestration/index.js +10 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/patterns.d.ts +159 -0
- package/dist/orchestration/patterns.d.ts.map +1 -0
- package/dist/orchestration/patterns.js +371 -0
- package/dist/orchestration/patterns.js.map +1 -0
- package/dist/orchestration/types.d.ts +146 -0
- package/dist/orchestration/types.d.ts.map +1 -0
- package/dist/orchestration/types.js +20 -0
- package/dist/orchestration/types.js.map +1 -0
- package/dist/rag/EnterpriseRetriever.d.ts +114 -0
- package/dist/rag/EnterpriseRetriever.d.ts.map +1 -0
- package/dist/rag/EnterpriseRetriever.js +240 -0
- package/dist/rag/EnterpriseRetriever.js.map +1 -0
- package/dist/rag/index.d.ts +6 -0
- package/dist/rag/index.d.ts.map +1 -0
- package/dist/rag/index.js +5 -0
- package/dist/rag/index.js.map +1 -0
- package/dist/retrieval/bm25.d.ts +48 -1
- package/dist/retrieval/bm25.d.ts.map +1 -1
- package/dist/retrieval/bm25.js +59 -13
- package/dist/retrieval/bm25.js.map +1 -1
- package/dist/retrieval/index.d.ts +2 -2
- package/dist/retrieval/index.d.ts.map +1 -1
- package/dist/retrieval/index.js +1 -1
- package/dist/retrieval/index.js.map +1 -1
- package/dist/telemetry/InstrumentedBridge.d.ts +62 -0
- package/dist/telemetry/InstrumentedBridge.d.ts.map +1 -0
- package/dist/telemetry/InstrumentedBridge.js +120 -0
- package/dist/telemetry/InstrumentedBridge.js.map +1 -0
- package/dist/telemetry/MetricsRegistry.d.ts +88 -0
- package/dist/telemetry/MetricsRegistry.d.ts.map +1 -0
- package/dist/telemetry/MetricsRegistry.js +167 -0
- package/dist/telemetry/MetricsRegistry.js.map +1 -0
- package/dist/telemetry/Tracer.d.ts +137 -0
- package/dist/telemetry/Tracer.d.ts.map +1 -0
- package/dist/telemetry/Tracer.js +244 -0
- package/dist/telemetry/Tracer.js.map +1 -0
- package/dist/telemetry/exporters.d.ts +68 -0
- package/dist/telemetry/exporters.d.ts.map +1 -0
- package/dist/telemetry/exporters.js +145 -0
- package/dist/telemetry/exporters.js.map +1 -0
- package/dist/telemetry/index.d.ts +19 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +12 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/pricing.d.ts +74 -0
- package/dist/telemetry/pricing.d.ts.map +1 -0
- package/dist/telemetry/pricing.js +98 -0
- package/dist/telemetry/pricing.js.map +1 -0
- package/dist/telemetry/traceparent.d.ts +50 -0
- package/dist/telemetry/traceparent.d.ts.map +1 -0
- package/dist/telemetry/traceparent.js +77 -0
- package/dist/telemetry/traceparent.js.map +1 -0
- package/dist/telemetry/types.d.ts +90 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/telemetry/types.js +17 -0
- package/dist/telemetry/types.js.map +1 -0
- package/dist/vectorstore/MemoryVectorStore.d.ts +72 -0
- package/dist/vectorstore/MemoryVectorStore.d.ts.map +1 -0
- package/dist/vectorstore/MemoryVectorStore.js +205 -0
- package/dist/vectorstore/MemoryVectorStore.js.map +1 -0
- package/dist/vectorstore/RestVectorStore.d.ts +79 -0
- package/dist/vectorstore/RestVectorStore.d.ts.map +1 -0
- package/dist/vectorstore/RestVectorStore.js +170 -0
- package/dist/vectorstore/RestVectorStore.js.map +1 -0
- package/dist/vectorstore/dialects.d.ts +101 -0
- package/dist/vectorstore/dialects.d.ts.map +1 -0
- package/dist/vectorstore/dialects.js +618 -0
- package/dist/vectorstore/dialects.js.map +1 -0
- package/dist/vectorstore/filter.d.ts +42 -0
- package/dist/vectorstore/filter.d.ts.map +1 -0
- package/dist/vectorstore/filter.js +132 -0
- package/dist/vectorstore/filter.js.map +1 -0
- package/dist/vectorstore/index.d.ts +15 -0
- package/dist/vectorstore/index.d.ts.map +1 -0
- package/dist/vectorstore/index.js +11 -0
- package/dist/vectorstore/index.js.map +1 -0
- package/dist/vectorstore/types.d.ts +151 -0
- package/dist/vectorstore/types.d.ts.map +1 -0
- package/dist/vectorstore/types.js +30 -0
- package/dist/vectorstore/types.js.map +1 -0
- package/package.json +39 -5
- package/src/bridges/AnthropicBridge.ts +68 -3
- package/src/bridges/CachingBridge.ts +28 -2
- package/src/bridges/OpenAIBridge.ts +48 -3
- package/src/bridges/SemanticCachingBridge.ts +29 -2
- package/src/bridges/TransformerBridge.ts +16 -0
- package/src/bridges/VertexAIBridge.ts +297 -0
- package/src/bridges/index.ts +3 -1
- package/src/eval/EvalHarness.ts +214 -0
- package/src/eval/graders.ts +218 -0
- package/src/eval/index.ts +26 -0
- package/src/eval/types.ts +107 -0
- package/src/evermind/contract.ts +39 -0
- package/src/evermind/index.ts +21 -0
- package/src/evermind/memoryBlock.ts +38 -0
- package/src/evermind/recall.ts +122 -0
- package/src/index.ts +232 -0
- package/src/ingest/IngestionPipeline.ts +378 -0
- package/src/ingest/index.ts +46 -0
- package/src/ingest/parsers.ts +318 -0
- package/src/ingest/types.ts +116 -0
- package/src/memory/MemoryStore.ts +55 -3
- package/src/memory/index.ts +1 -1
- package/src/orchestration/AgentGraph.ts +462 -0
- package/src/orchestration/checkpoint.ts +129 -0
- package/src/orchestration/index.ts +63 -0
- package/src/orchestration/patterns.ts +532 -0
- package/src/orchestration/types.ts +128 -0
- package/src/rag/EnterpriseRetriever.ts +348 -0
- package/src/rag/index.ts +18 -0
- package/src/retrieval/bm25.ts +71 -13
- package/src/retrieval/index.ts +2 -2
- package/src/telemetry/InstrumentedBridge.ts +152 -0
- package/src/telemetry/MetricsRegistry.ts +263 -0
- package/src/telemetry/Tracer.ts +310 -0
- package/src/telemetry/exporters.ts +187 -0
- package/src/telemetry/index.ts +56 -0
- package/src/telemetry/pricing.ts +129 -0
- package/src/telemetry/traceparent.ts +89 -0
- package/src/telemetry/types.ts +105 -0
- package/src/vectorstore/MemoryVectorStore.ts +250 -0
- package/src/vectorstore/RestVectorStore.ts +236 -0
- package/src/vectorstore/dialects.ts +702 -0
- package/src/vectorstore/filter.ts +148 -0
- package/src/vectorstore/index.ts +53 -0
- package/src/vectorstore/types.ts +149 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vectorstore/filter.ts — the ONE evaluator and the ONE scope compiler.
|
|
3
|
+
*
|
|
4
|
+
* Two rules this file exists to enforce:
|
|
5
|
+
*
|
|
6
|
+
* • A filter means the same thing everywhere. The in-process store, the
|
|
7
|
+
* post-filter a remote adapter applies when its dialect cannot express a
|
|
8
|
+
* clause, the ingest de-duplicator and the eval harness all call
|
|
9
|
+
* {@link matchesFilter}. A second implementation would eventually disagree
|
|
10
|
+
* about `null` or about a missing field, and a filter that disagrees with
|
|
11
|
+
* itself across two code paths is a cross-tenant leak waiting for traffic.
|
|
12
|
+
*
|
|
13
|
+
* • Access scope is compiled, never hand-written. {@link accessFilter} is the
|
|
14
|
+
* single definition of "what may this caller see", so a new adapter or a new
|
|
15
|
+
* query path inherits the rule instead of re-deriving it.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
ACL_FIELD,
|
|
20
|
+
ACL_PUBLIC,
|
|
21
|
+
SENSITIVITY_FIELD,
|
|
22
|
+
TENANT_FIELD,
|
|
23
|
+
type AccessScope,
|
|
24
|
+
type MetadataFilter,
|
|
25
|
+
type MetadataRecord,
|
|
26
|
+
type MetadataValue,
|
|
27
|
+
} from './types.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Compiles an access scope into a filter.
|
|
31
|
+
*
|
|
32
|
+
* The ACL clause is `anyOf(acl, [...principals, '*'])`. Records are required to
|
|
33
|
+
* carry a non-empty `acl` (ingestion defaults it to `['*']`), which is what makes
|
|
34
|
+
* the rule fail CLOSED: a record written without an ACL matches nothing rather
|
|
35
|
+
* than matching everyone.
|
|
36
|
+
*/
|
|
37
|
+
export function accessFilter(scope: AccessScope): MetadataFilter {
|
|
38
|
+
const clauses: MetadataFilter[] = [
|
|
39
|
+
{ op: 'eq', field: TENANT_FIELD, value: scope.tenantId },
|
|
40
|
+
{ op: 'anyOf', field: ACL_FIELD, values: [...(scope.principals ?? []), ACL_PUBLIC] },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
if (scope.maxSensitivity !== undefined) {
|
|
44
|
+
clauses.push({ op: 'lte', field: SENSITIVITY_FIELD, value: scope.maxSensitivity });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { op: 'and', filters: clauses };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* ANDs a caller filter with a scope, dropping either when absent.
|
|
52
|
+
* Returns `undefined` only when neither is present — an unscoped query, which is
|
|
53
|
+
* legitimate for single-tenant use but never happens by accident, because the
|
|
54
|
+
* caller had to omit the scope explicitly.
|
|
55
|
+
*/
|
|
56
|
+
export function combineFilters(
|
|
57
|
+
filter?: MetadataFilter,
|
|
58
|
+
scope?: AccessScope,
|
|
59
|
+
): MetadataFilter | undefined {
|
|
60
|
+
const scoped = scope ? accessFilter(scope) : undefined;
|
|
61
|
+
if (filter && scoped) return { op: 'and', filters: [scoped, filter] };
|
|
62
|
+
return filter ?? scoped;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Evaluates a filter against one record's metadata. */
|
|
66
|
+
export function matchesFilter(metadata: MetadataRecord, filter?: MetadataFilter): boolean {
|
|
67
|
+
if (!filter) return true;
|
|
68
|
+
|
|
69
|
+
switch (filter.op) {
|
|
70
|
+
case 'and':
|
|
71
|
+
return filter.filters.every((f) => matchesFilter(metadata, f));
|
|
72
|
+
case 'or':
|
|
73
|
+
return filter.filters.some((f) => matchesFilter(metadata, f));
|
|
74
|
+
case 'not':
|
|
75
|
+
return !matchesFilter(metadata, filter.filter);
|
|
76
|
+
case 'exists':
|
|
77
|
+
return metadata[filter.field] !== undefined && metadata[filter.field] !== null;
|
|
78
|
+
case 'in':
|
|
79
|
+
return filter.values.some((v) => scalarEquals(metadata[filter.field], v));
|
|
80
|
+
case 'nin':
|
|
81
|
+
return !filter.values.some((v) => scalarEquals(metadata[filter.field], v));
|
|
82
|
+
case 'anyOf': {
|
|
83
|
+
const actual = metadata[filter.field];
|
|
84
|
+
if (actual === undefined || actual === null) return false;
|
|
85
|
+
const held = Array.isArray(actual) ? actual : [actual];
|
|
86
|
+
return held.some((h) => filter.values.some((v) => scalarEquals(h, v)));
|
|
87
|
+
}
|
|
88
|
+
case 'contains': {
|
|
89
|
+
const actual = metadata[filter.field];
|
|
90
|
+
if (typeof actual !== 'string') return false;
|
|
91
|
+
return actual.toLowerCase().includes(filter.value.toLowerCase());
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
return compare(metadata[filter.field], filter.op, filter.value);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function scalarEquals(a: MetadataValue | undefined, b: MetadataValue): boolean {
|
|
99
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
100
|
+
return Array.isArray(a) && Array.isArray(b)
|
|
101
|
+
&& a.length === b.length
|
|
102
|
+
&& a.every((v, i) => v === b[i]);
|
|
103
|
+
}
|
|
104
|
+
return a === b;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function compare(
|
|
108
|
+
actual: MetadataValue | undefined,
|
|
109
|
+
op: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte',
|
|
110
|
+
expected: MetadataValue,
|
|
111
|
+
): boolean {
|
|
112
|
+
if (op === 'eq') return scalarEquals(actual, expected);
|
|
113
|
+
if (op === 'ne') return !scalarEquals(actual, expected);
|
|
114
|
+
|
|
115
|
+
// Ordering comparisons on a missing or non-scalar field are false rather than
|
|
116
|
+
// throwing: a record without the field simply does not satisfy `sensitivity <= n`.
|
|
117
|
+
if (actual === undefined || actual === null) return false;
|
|
118
|
+
if (Array.isArray(actual) || Array.isArray(expected) || expected === null) return false;
|
|
119
|
+
if (typeof actual !== typeof expected) return false;
|
|
120
|
+
if (typeof actual === 'boolean' || typeof expected === 'boolean') return false;
|
|
121
|
+
|
|
122
|
+
switch (op) {
|
|
123
|
+
case 'gt': return actual > expected;
|
|
124
|
+
case 'gte': return actual >= expected;
|
|
125
|
+
case 'lt': return actual < expected;
|
|
126
|
+
default: return actual <= expected;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Walks a filter tree, reporting every field it touches. Adapters use this to
|
|
132
|
+
* decide up front whether a dialect can push the whole filter down, or whether a
|
|
133
|
+
* post-filter pass is needed.
|
|
134
|
+
*/
|
|
135
|
+
export function filterFields(filter: MetadataFilter, out = new Set<string>()): Set<string> {
|
|
136
|
+
switch (filter.op) {
|
|
137
|
+
case 'and':
|
|
138
|
+
case 'or':
|
|
139
|
+
for (const f of filter.filters) filterFields(f, out);
|
|
140
|
+
break;
|
|
141
|
+
case 'not':
|
|
142
|
+
filterFields(filter.filter, out);
|
|
143
|
+
break;
|
|
144
|
+
default:
|
|
145
|
+
out.add(filter.field);
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vectorstore — the VectorStore port, its declarative filter algebra, and the
|
|
3
|
+
* adapters. Tenant/ACL scope is compiled into every read, so multi-tenant RAG
|
|
4
|
+
* fails closed rather than depending on each caller remembering to scope.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { MemoryVectorStore } from './MemoryVectorStore.js';
|
|
8
|
+
export type { MemoryVectorStoreOptions } from './MemoryVectorStore.js';
|
|
9
|
+
|
|
10
|
+
export { RestVectorStore } from './RestVectorStore.js';
|
|
11
|
+
export type { RestVectorStoreOptions } from './RestVectorStore.js';
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
getDialect,
|
|
15
|
+
listDialects,
|
|
16
|
+
registerDialect,
|
|
17
|
+
evermindDialect,
|
|
18
|
+
qdrantDialect,
|
|
19
|
+
pineconeDialect,
|
|
20
|
+
vertexAiDialect,
|
|
21
|
+
} from './dialects.js';
|
|
22
|
+
export type {
|
|
23
|
+
VectorDialect,
|
|
24
|
+
DialectContext,
|
|
25
|
+
HttpRequest,
|
|
26
|
+
TranslatedFilter,
|
|
27
|
+
} from './dialects.js';
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
accessFilter,
|
|
31
|
+
combineFilters,
|
|
32
|
+
matchesFilter,
|
|
33
|
+
filterFields,
|
|
34
|
+
} from './filter.js';
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
TENANT_FIELD,
|
|
38
|
+
ACL_FIELD,
|
|
39
|
+
SENSITIVITY_FIELD,
|
|
40
|
+
ACL_PUBLIC,
|
|
41
|
+
} from './types.js';
|
|
42
|
+
export type {
|
|
43
|
+
AccessScope,
|
|
44
|
+
ComparisonOp,
|
|
45
|
+
MetadataFilter,
|
|
46
|
+
MetadataRecord,
|
|
47
|
+
MetadataValue,
|
|
48
|
+
UpsertResult,
|
|
49
|
+
VectorMatch,
|
|
50
|
+
VectorQuery,
|
|
51
|
+
VectorRecord,
|
|
52
|
+
VectorStore,
|
|
53
|
+
} from './types.js';
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vectorstore/types.ts — the VectorStore port.
|
|
3
|
+
*
|
|
4
|
+
* Enterprise deployments do not get to choose the vector database; the customer
|
|
5
|
+
* already has one (pgvector in their Cloud SQL, Vertex AI Vector Search because
|
|
6
|
+
* they are a GCP shop, Qdrant because platform standardised on it). So the store
|
|
7
|
+
* is a PORT, and every adapter is infrastructure behind it. Retrieval, ingestion
|
|
8
|
+
* and evaluation depend on this interface and never on a vendor.
|
|
9
|
+
*
|
|
10
|
+
* Two things make it enterprise-grade rather than a toy vector index:
|
|
11
|
+
*
|
|
12
|
+
* 1. **Filters are DATA, not predicates.** A `(record) => boolean` cannot be
|
|
13
|
+
* pushed down to a remote database — it forces fetch-everything-then-filter,
|
|
14
|
+
* which is both slow and a data-leak: rows the caller may not see are read
|
|
15
|
+
* into the process before being dropped. {@link MetadataFilter} is a small
|
|
16
|
+
* declarative algebra each adapter translates into its own query language.
|
|
17
|
+
*
|
|
18
|
+
* 2. **Access scope is first-class.** Multi-tenant RAG fails closed only if the
|
|
19
|
+
* tenant predicate is part of the query the database runs. {@link AccessScope}
|
|
20
|
+
* compiles to a filter that is AND-ed into every read, so "forgot to scope the
|
|
21
|
+
* query" is not a reachable state.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export type MetadataValue = string | number | boolean | null | string[];
|
|
25
|
+
|
|
26
|
+
export type MetadataRecord = Record<string, MetadataValue>;
|
|
27
|
+
|
|
28
|
+
/** A stored unit of retrievable knowledge. */
|
|
29
|
+
export interface VectorRecord {
|
|
30
|
+
id: string;
|
|
31
|
+
/** The chunk text — returned with matches so a retriever can rerank on it. */
|
|
32
|
+
text: string;
|
|
33
|
+
/** Embedding. Optional so a store can embed server-side. */
|
|
34
|
+
vector?: Float32Array;
|
|
35
|
+
metadata: MetadataRecord;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface VectorMatch {
|
|
39
|
+
id: string;
|
|
40
|
+
text: string;
|
|
41
|
+
/** Similarity in [0,1] — cosine for every adapter here. */
|
|
42
|
+
score: number;
|
|
43
|
+
metadata: MetadataRecord;
|
|
44
|
+
vector?: Float32Array;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Filter algebra ────────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
export type ComparisonOp = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
|
|
50
|
+
|
|
51
|
+
export type MetadataFilter =
|
|
52
|
+
| { op: ComparisonOp; field: string; value: MetadataValue }
|
|
53
|
+
/** Field equals any of `values` (scalar field). */
|
|
54
|
+
| { op: 'in' | 'nin'; field: string; values: MetadataValue[] }
|
|
55
|
+
/**
|
|
56
|
+
* Field (an array, or a scalar treated as a one-element array) intersects
|
|
57
|
+
* `values`. This is the ACL primitive — every mainstream vector database can
|
|
58
|
+
* express "array field matches any of", which is why ACLs are modelled as an
|
|
59
|
+
* array rather than as a bitmask or a nested object.
|
|
60
|
+
*/
|
|
61
|
+
| { op: 'anyOf'; field: string; values: MetadataValue[] }
|
|
62
|
+
/** Case-insensitive substring match on a string field. */
|
|
63
|
+
| { op: 'contains'; field: string; value: string }
|
|
64
|
+
| { op: 'exists'; field: string }
|
|
65
|
+
// `and` and `or` are separate members rather than one `op: 'and' | 'or'`
|
|
66
|
+
// variant so that a discriminant check narrows them away cleanly; a union-typed
|
|
67
|
+
// discriminant is not removable by control flow, which forces casts on every
|
|
68
|
+
// dialect that walks the tree.
|
|
69
|
+
| { op: 'and'; filters: MetadataFilter[] }
|
|
70
|
+
| { op: 'or'; filters: MetadataFilter[] }
|
|
71
|
+
| { op: 'not'; filter: MetadataFilter };
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The non-composite half of the algebra — every variant that names a `field`.
|
|
75
|
+
* Dialect translation works leaf-by-leaf, so having the type say so removes a
|
|
76
|
+
* cast from every adapter.
|
|
77
|
+
*/
|
|
78
|
+
export type LeafFilter = Extract<MetadataFilter, { field: string }>;
|
|
79
|
+
|
|
80
|
+
// ── Access scope ──────────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
/** Metadata keys the access model reserves. Ingestion always populates them. */
|
|
83
|
+
export const TENANT_FIELD = 'tenantId';
|
|
84
|
+
export const ACL_FIELD = 'acl';
|
|
85
|
+
export const SENSITIVITY_FIELD = 'sensitivity';
|
|
86
|
+
/** ACL entry meaning "any principal within the tenant". */
|
|
87
|
+
export const ACL_PUBLIC = '*';
|
|
88
|
+
|
|
89
|
+
export interface AccessScope {
|
|
90
|
+
/** Hard tenant boundary. Every read is AND-ed with an equality on this. */
|
|
91
|
+
tenantId: string;
|
|
92
|
+
/**
|
|
93
|
+
* Group / role / user ids the caller holds. A record is visible when its `acl`
|
|
94
|
+
* array intersects these, or contains {@link ACL_PUBLIC}. Omit for
|
|
95
|
+
* tenant-wide access to public records only.
|
|
96
|
+
*/
|
|
97
|
+
principals?: string[];
|
|
98
|
+
/**
|
|
99
|
+
* Highest classification level the caller may read (records carry a numeric
|
|
100
|
+
* `sensitivity`). Omit for no ceiling.
|
|
101
|
+
*/
|
|
102
|
+
maxSensitivity?: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── The port ──────────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
export interface VectorQuery {
|
|
108
|
+
/** Dense query embedding. Required unless the adapter embeds server-side. */
|
|
109
|
+
vector?: Float32Array;
|
|
110
|
+
/** Raw query text — used by `keywordSearch`, and by server-side embedders. */
|
|
111
|
+
text?: string;
|
|
112
|
+
/** Results to return. Default 10. */
|
|
113
|
+
topK?: number;
|
|
114
|
+
/** Declarative metadata predicate, pushed down to the store. */
|
|
115
|
+
filter?: MetadataFilter;
|
|
116
|
+
/** Tenant/ACL scope, AND-ed with `filter`. */
|
|
117
|
+
scope?: AccessScope;
|
|
118
|
+
/** Drop matches scoring below this. Default 0. */
|
|
119
|
+
minScore?: number;
|
|
120
|
+
/** Return stored vectors on matches (costs bandwidth). Default false. */
|
|
121
|
+
includeVectors?: boolean;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface UpsertResult {
|
|
125
|
+
upserted: number;
|
|
126
|
+
/** Ids the store considered unchanged and skipped, when it can tell. */
|
|
127
|
+
skipped: number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The port. `keywordSearch` is optional because not every vector database has a
|
|
132
|
+
* lexical index; retrievers degrade to dense-only and SAY SO on the span rather
|
|
133
|
+
* than silently returning worse results.
|
|
134
|
+
*/
|
|
135
|
+
export interface VectorStore {
|
|
136
|
+
/** Adapter name, stamped on telemetry spans. */
|
|
137
|
+
readonly name: string;
|
|
138
|
+
|
|
139
|
+
upsert(records: readonly VectorRecord[]): Promise<UpsertResult>;
|
|
140
|
+
query(query: VectorQuery): Promise<VectorMatch[]>;
|
|
141
|
+
delete(ids: readonly string[]): Promise<number>;
|
|
142
|
+
/** Bulk delete by predicate — how an incremental sync removes a stale source. */
|
|
143
|
+
deleteByFilter(filter: MetadataFilter, scope?: AccessScope): Promise<number>;
|
|
144
|
+
fetch(ids: readonly string[]): Promise<VectorRecord[]>;
|
|
145
|
+
count(filter?: MetadataFilter, scope?: AccessScope): Promise<number>;
|
|
146
|
+
|
|
147
|
+
/** Lexical/BM25 search, when the adapter supports one. */
|
|
148
|
+
keywordSearch?(query: VectorQuery): Promise<VectorMatch[]>;
|
|
149
|
+
}
|