agentic-qe 3.7.21 → 3.8.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/.claude/helpers/brain-checkpoint.cjs +4 -1
- package/.claude/helpers/statusline-v3.cjs +3 -1
- package/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +45 -0
- package/README.md +2 -14
- package/assets/helpers/statusline-v3.cjs +3 -1
- package/dist/cli/brain-commands.js +6 -10
- package/dist/cli/bundle.js +7441 -4327
- package/dist/cli/commands/audit.d.ts +43 -0
- package/dist/cli/commands/audit.js +125 -0
- package/dist/cli/commands/hooks.js +29 -6
- package/dist/cli/commands/init.js +1 -73
- package/dist/cli/commands/learning.js +270 -13
- package/dist/cli/commands/ruvector-commands.d.ts +15 -0
- package/dist/cli/commands/ruvector-commands.js +271 -0
- package/dist/cli/handlers/init-handler.d.ts +0 -1
- package/dist/cli/handlers/init-handler.js +0 -6
- package/dist/cli/index.js +4 -2
- package/dist/context/sources/defect-source.js +2 -2
- package/dist/context/sources/memory-source.js +2 -2
- package/dist/context/sources/requirements-source.js +2 -2
- package/dist/coordination/behavior-tree/decorators.d.ts +108 -0
- package/dist/coordination/behavior-tree/decorators.js +251 -0
- package/dist/coordination/behavior-tree/index.d.ts +12 -0
- package/dist/coordination/behavior-tree/index.js +15 -0
- package/dist/coordination/behavior-tree/nodes.d.ts +165 -0
- package/dist/coordination/behavior-tree/nodes.js +338 -0
- package/dist/coordination/behavior-tree/qe-trees.d.ts +105 -0
- package/dist/coordination/behavior-tree/qe-trees.js +181 -0
- package/dist/coordination/coherence-action-gate.d.ts +284 -0
- package/dist/coordination/coherence-action-gate.js +512 -0
- package/dist/coordination/index.d.ts +4 -0
- package/dist/coordination/index.js +8 -0
- package/dist/coordination/reasoning-qec.d.ts +315 -0
- package/dist/coordination/reasoning-qec.js +585 -0
- package/dist/coordination/task-executor.d.ts +16 -0
- package/dist/coordination/task-executor.js +99 -0
- package/dist/coordination/workflow-orchestrator.d.ts +29 -0
- package/dist/coordination/workflow-orchestrator.js +42 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.d.ts +135 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.js +327 -0
- package/dist/domains/visual-accessibility/index.d.ts +1 -0
- package/dist/domains/visual-accessibility/index.js +4 -0
- package/dist/governance/coherence-validator.d.ts +112 -0
- package/dist/governance/coherence-validator.js +180 -0
- package/dist/governance/index.d.ts +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/witness-chain.d.ts +311 -0
- package/dist/governance/witness-chain.js +509 -0
- package/dist/init/index.d.ts +0 -2
- package/dist/init/index.js +0 -1
- package/dist/init/init-wizard-steps.d.ts +10 -0
- package/dist/init/init-wizard-steps.js +87 -1
- package/dist/init/init-wizard.d.ts +1 -9
- package/dist/init/init-wizard.js +3 -69
- package/dist/init/orchestrator.js +0 -1
- package/dist/init/phases/01-detection.js +0 -27
- package/dist/init/phases/07-hooks.js +6 -4
- package/dist/init/phases/phase-interface.d.ts +0 -1
- package/dist/init/settings-merge.js +1 -1
- package/dist/integrations/browser/qe-dashboard/clustering.d.ts +48 -0
- package/dist/integrations/browser/qe-dashboard/clustering.js +183 -0
- package/dist/integrations/browser/qe-dashboard/index.d.ts +12 -0
- package/dist/integrations/browser/qe-dashboard/index.js +15 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.d.ts +165 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.js +260 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.d.ts +144 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.js +277 -0
- package/dist/integrations/ruvector/cognitive-container-codec.d.ts +51 -0
- package/dist/integrations/ruvector/cognitive-container-codec.js +180 -0
- package/dist/integrations/ruvector/cognitive-container.d.ts +125 -0
- package/dist/integrations/ruvector/cognitive-container.js +306 -0
- package/dist/integrations/ruvector/coherence-gate.d.ts +309 -0
- package/dist/integrations/ruvector/coherence-gate.js +631 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.d.ts +176 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.js +301 -0
- package/dist/integrations/ruvector/dither-adapter.d.ts +122 -0
- package/dist/integrations/ruvector/dither-adapter.js +295 -0
- package/dist/integrations/ruvector/domain-transfer.d.ts +129 -0
- package/dist/integrations/ruvector/domain-transfer.js +220 -0
- package/dist/integrations/ruvector/feature-flags.d.ts +214 -2
- package/dist/integrations/ruvector/feature-flags.js +167 -2
- package/dist/integrations/ruvector/filter-adapter.d.ts +71 -0
- package/dist/integrations/ruvector/filter-adapter.js +285 -0
- package/dist/integrations/ruvector/gnn-wrapper.d.ts +20 -0
- package/dist/integrations/ruvector/gnn-wrapper.js +40 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.d.ts +237 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.js +394 -0
- package/dist/integrations/ruvector/index.d.ts +8 -2
- package/dist/integrations/ruvector/index.js +18 -2
- package/dist/integrations/ruvector/interfaces.d.ts +40 -0
- package/dist/integrations/ruvector/sona-persistence.d.ts +54 -0
- package/dist/integrations/ruvector/sona-persistence.js +162 -0
- package/dist/integrations/ruvector/sona-three-loop.d.ts +392 -0
- package/dist/integrations/ruvector/sona-three-loop.js +814 -0
- package/dist/integrations/ruvector/sona-wrapper.d.ts +97 -0
- package/dist/integrations/ruvector/sona-wrapper.js +147 -3
- package/dist/integrations/ruvector/spectral-math.d.ts +101 -0
- package/dist/integrations/ruvector/spectral-math.js +254 -0
- package/dist/integrations/ruvector/temporal-compression.d.ts +163 -0
- package/dist/integrations/ruvector/temporal-compression.js +318 -0
- package/dist/integrations/ruvector/thompson-sampler.d.ts +61 -0
- package/dist/integrations/ruvector/thompson-sampler.js +118 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.d.ts +80 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.js +63 -0
- package/dist/integrations/ruvector/transfer-verification.d.ts +119 -0
- package/dist/integrations/ruvector/transfer-verification.js +115 -0
- package/dist/kernel/hnsw-adapter.d.ts +52 -1
- package/dist/kernel/hnsw-adapter.js +139 -4
- package/dist/kernel/hnsw-index-provider.d.ts +5 -0
- package/dist/kernel/native-hnsw-backend.d.ts +110 -0
- package/dist/kernel/native-hnsw-backend.js +408 -0
- package/dist/kernel/unified-memory.js +5 -6
- package/dist/learning/aqe-learning-engine.d.ts +2 -0
- package/dist/learning/aqe-learning-engine.js +65 -0
- package/dist/learning/experience-capture-middleware.js +20 -0
- package/dist/learning/experience-capture.d.ts +10 -0
- package/dist/learning/experience-capture.js +34 -0
- package/dist/learning/index.d.ts +2 -2
- package/dist/learning/index.js +4 -4
- package/dist/learning/metrics-tracker.d.ts +11 -0
- package/dist/learning/metrics-tracker.js +29 -13
- package/dist/learning/pattern-lifecycle.d.ts +30 -1
- package/dist/learning/pattern-lifecycle.js +92 -20
- package/dist/learning/pattern-store.d.ts +8 -0
- package/dist/learning/pattern-store.js +8 -2
- package/dist/learning/qe-unified-memory.js +1 -28
- package/dist/learning/regret-tracker.d.ts +201 -0
- package/dist/learning/regret-tracker.js +361 -0
- package/dist/mcp/bundle.js +5915 -474
- package/dist/routing/index.d.ts +4 -2
- package/dist/routing/index.js +3 -1
- package/dist/routing/neural-tiny-dancer-router.d.ts +268 -0
- package/dist/routing/neural-tiny-dancer-router.js +514 -0
- package/dist/routing/queen-integration.js +5 -5
- package/dist/routing/routing-config.d.ts +6 -0
- package/dist/routing/routing-config.js +1 -0
- package/dist/routing/simple-neural-router.d.ts +76 -0
- package/dist/routing/simple-neural-router.js +202 -0
- package/dist/routing/tiny-dancer-router.d.ts +20 -1
- package/dist/routing/tiny-dancer-router.js +21 -2
- package/dist/test-scheduling/dag-attention-scheduler.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-scheduler.js +358 -0
- package/dist/test-scheduling/dag-attention-types.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-types.js +10 -0
- package/dist/test-scheduling/index.d.ts +1 -0
- package/dist/test-scheduling/index.js +4 -0
- package/dist/test-scheduling/pipeline.d.ts +8 -0
- package/dist/test-scheduling/pipeline.js +28 -0
- package/package.json +6 -2
- package/dist/cli/commands/migrate.d.ts +0 -9
- package/dist/cli/commands/migrate.js +0 -566
- package/dist/init/init-wizard-migration.d.ts +0 -52
- package/dist/init/init-wizard-migration.js +0 -345
- package/dist/init/migration/config-migrator.d.ts +0 -31
- package/dist/init/migration/config-migrator.js +0 -149
- package/dist/init/migration/data-migrator.d.ts +0 -72
- package/dist/init/migration/data-migrator.js +0 -232
- package/dist/init/migration/detector.d.ts +0 -44
- package/dist/init/migration/detector.js +0 -105
- package/dist/init/migration/index.d.ts +0 -8
- package/dist/init/migration/index.js +0 -8
- package/dist/learning/v2-to-v3-migration.d.ts +0 -86
- package/dist/learning/v2-to-v3-migration.js +0 -529
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - RuVector Metadata Filtering Adapter
|
|
3
|
+
* Task 1.2: Metadata Filtering Layer (ruvector-filter)
|
|
4
|
+
*
|
|
5
|
+
* Provides composable filter expressions (AND/OR/NOT/FIELD) that can be
|
|
6
|
+
* applied to pattern search results for rich post-search filtering.
|
|
7
|
+
*
|
|
8
|
+
* TypeScript in-memory implementation (no native package exists).
|
|
9
|
+
* Backward compatible: no filter = no change to search results.
|
|
10
|
+
*
|
|
11
|
+
* @module integrations/ruvector/filter-adapter
|
|
12
|
+
*/
|
|
13
|
+
import { getRuVectorFeatureFlags } from './feature-flags.js';
|
|
14
|
+
let nativeEngine = null;
|
|
15
|
+
let nativeChecked = false;
|
|
16
|
+
/**
|
|
17
|
+
* Check for native filter engine.
|
|
18
|
+
* No native package exists — always returns null.
|
|
19
|
+
* The TypeScript implementation is the production implementation.
|
|
20
|
+
*/
|
|
21
|
+
async function loadNativeEngine() {
|
|
22
|
+
if (nativeChecked)
|
|
23
|
+
return nativeEngine;
|
|
24
|
+
nativeChecked = true;
|
|
25
|
+
nativeEngine = null;
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Filter Builder Helpers
|
|
30
|
+
// ============================================================================
|
|
31
|
+
/**
|
|
32
|
+
* Create an AND filter combining multiple sub-expressions
|
|
33
|
+
*/
|
|
34
|
+
export function and(...children) {
|
|
35
|
+
return { type: 'AND', children };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create an OR filter combining multiple sub-expressions
|
|
39
|
+
*/
|
|
40
|
+
export function or(...children) {
|
|
41
|
+
return { type: 'OR', children };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a NOT filter inverting a sub-expression
|
|
45
|
+
*/
|
|
46
|
+
export function not(child) {
|
|
47
|
+
return { type: 'NOT', child };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a field-level filter expression
|
|
51
|
+
*/
|
|
52
|
+
export function field(fieldName, operator, value) {
|
|
53
|
+
return { type: 'FIELD', field: fieldName, operator, value };
|
|
54
|
+
}
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// TypeScript Fallback Filter Engine
|
|
57
|
+
// ============================================================================
|
|
58
|
+
/**
|
|
59
|
+
* Resolve a nested field path (e.g., "context.tags") on a PatternSearchResult.
|
|
60
|
+
* Looks first on the pattern object, then on the result itself.
|
|
61
|
+
*/
|
|
62
|
+
function resolveField(result, fieldPath) {
|
|
63
|
+
const pattern = result.pattern;
|
|
64
|
+
// Try pattern first, then result-level fields
|
|
65
|
+
const targets = [pattern, result];
|
|
66
|
+
for (const target of targets) {
|
|
67
|
+
const parts = fieldPath.split('.');
|
|
68
|
+
let current = target;
|
|
69
|
+
for (const part of parts) {
|
|
70
|
+
if (current === null || current === undefined)
|
|
71
|
+
break;
|
|
72
|
+
if (typeof current === 'object') {
|
|
73
|
+
current = current[part];
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
current = undefined;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (current !== undefined)
|
|
81
|
+
return current;
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Evaluate a single FIELD filter against a resolved value
|
|
87
|
+
*/
|
|
88
|
+
function evaluateFieldFilter(resolvedValue, operator, filterValue) {
|
|
89
|
+
switch (operator) {
|
|
90
|
+
case 'eq':
|
|
91
|
+
return resolvedValue === filterValue;
|
|
92
|
+
case 'gt':
|
|
93
|
+
return (typeof resolvedValue === 'number' &&
|
|
94
|
+
typeof filterValue === 'number' &&
|
|
95
|
+
resolvedValue > filterValue);
|
|
96
|
+
case 'lt':
|
|
97
|
+
return (typeof resolvedValue === 'number' &&
|
|
98
|
+
typeof filterValue === 'number' &&
|
|
99
|
+
resolvedValue < filterValue);
|
|
100
|
+
case 'gte':
|
|
101
|
+
return (typeof resolvedValue === 'number' &&
|
|
102
|
+
typeof filterValue === 'number' &&
|
|
103
|
+
resolvedValue >= filterValue);
|
|
104
|
+
case 'lte':
|
|
105
|
+
return (typeof resolvedValue === 'number' &&
|
|
106
|
+
typeof filterValue === 'number' &&
|
|
107
|
+
resolvedValue <= filterValue);
|
|
108
|
+
case 'in': {
|
|
109
|
+
if (!Array.isArray(filterValue))
|
|
110
|
+
return false;
|
|
111
|
+
return filterValue.includes(resolvedValue);
|
|
112
|
+
}
|
|
113
|
+
case 'contains': {
|
|
114
|
+
// Array.includes or string.includes
|
|
115
|
+
if (Array.isArray(resolvedValue)) {
|
|
116
|
+
return resolvedValue.includes(filterValue);
|
|
117
|
+
}
|
|
118
|
+
if (typeof resolvedValue === 'string' && typeof filterValue === 'string') {
|
|
119
|
+
return resolvedValue.includes(filterValue);
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
case 'between': {
|
|
124
|
+
if (typeof resolvedValue !== 'number' ||
|
|
125
|
+
!Array.isArray(filterValue) ||
|
|
126
|
+
filterValue.length !== 2) {
|
|
127
|
+
// Also support Date-based between
|
|
128
|
+
if (resolvedValue instanceof Date && Array.isArray(filterValue) && filterValue.length === 2) {
|
|
129
|
+
const ts = resolvedValue.getTime();
|
|
130
|
+
const lo = filterValue[0] instanceof Date ? filterValue[0].getTime() : Number(filterValue[0]);
|
|
131
|
+
const hi = filterValue[1] instanceof Date ? filterValue[1].getTime() : Number(filterValue[1]);
|
|
132
|
+
return ts >= lo && ts <= hi;
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const [lo, hi] = filterValue;
|
|
137
|
+
return resolvedValue >= lo && resolvedValue <= hi;
|
|
138
|
+
}
|
|
139
|
+
default:
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Evaluate a filter expression against a single search result.
|
|
145
|
+
* Returns true if the result passes the filter.
|
|
146
|
+
*/
|
|
147
|
+
export function evaluateFilter(result, filter) {
|
|
148
|
+
switch (filter.type) {
|
|
149
|
+
case 'AND': {
|
|
150
|
+
const children = filter.children ?? [];
|
|
151
|
+
return children.every((child) => evaluateFilter(result, child));
|
|
152
|
+
}
|
|
153
|
+
case 'OR': {
|
|
154
|
+
const children = filter.children ?? [];
|
|
155
|
+
if (children.length === 0)
|
|
156
|
+
return true;
|
|
157
|
+
return children.some((child) => evaluateFilter(result, child));
|
|
158
|
+
}
|
|
159
|
+
case 'NOT': {
|
|
160
|
+
if (!filter.child)
|
|
161
|
+
return true;
|
|
162
|
+
return !evaluateFilter(result, filter.child);
|
|
163
|
+
}
|
|
164
|
+
case 'FIELD': {
|
|
165
|
+
if (!filter.field || !filter.operator)
|
|
166
|
+
return true;
|
|
167
|
+
const resolved = resolveField(result, filter.field);
|
|
168
|
+
return evaluateFieldFilter(resolved, filter.operator, filter.value);
|
|
169
|
+
}
|
|
170
|
+
default:
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// Public API
|
|
176
|
+
// ============================================================================
|
|
177
|
+
/**
|
|
178
|
+
* Apply a filter expression to pattern search results.
|
|
179
|
+
*
|
|
180
|
+
* If the `useMetadataFiltering` feature flag is disabled, returns results unchanged.
|
|
181
|
+
* If the native `ruvector-filter` engine is available, delegates to it.
|
|
182
|
+
* Otherwise, applies the TypeScript fallback engine.
|
|
183
|
+
*
|
|
184
|
+
* @param results - Pattern search results to filter
|
|
185
|
+
* @param filter - Composable filter expression
|
|
186
|
+
* @returns Filtered results (subset of input, order preserved)
|
|
187
|
+
*/
|
|
188
|
+
export async function applyFilter(results, filter) {
|
|
189
|
+
// No filter = passthrough
|
|
190
|
+
if (!filter)
|
|
191
|
+
return results;
|
|
192
|
+
// Check feature flag
|
|
193
|
+
const flags = getRuVectorFeatureFlags();
|
|
194
|
+
if (!flags.useMetadataFiltering)
|
|
195
|
+
return results;
|
|
196
|
+
// Try native engine first
|
|
197
|
+
const native = await loadNativeEngine();
|
|
198
|
+
if (native) {
|
|
199
|
+
return native.applyFilter(results, filter);
|
|
200
|
+
}
|
|
201
|
+
// TypeScript fallback: in-memory filtering
|
|
202
|
+
return applyFilterSync(results, filter);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Synchronous version of applyFilter using the TypeScript fallback engine.
|
|
206
|
+
* Useful when native engine availability is already known to be absent.
|
|
207
|
+
*
|
|
208
|
+
* @param results - Pattern search results to filter
|
|
209
|
+
* @param filter - Composable filter expression
|
|
210
|
+
* @returns Filtered results (subset of input, order preserved)
|
|
211
|
+
*/
|
|
212
|
+
export function applyFilterSync(results, filter) {
|
|
213
|
+
if (!filter)
|
|
214
|
+
return results;
|
|
215
|
+
return results.filter((result) => evaluateFilter(result, filter));
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Validate a filter expression for structural correctness.
|
|
219
|
+
*
|
|
220
|
+
* @param filter - Filter expression to validate
|
|
221
|
+
* @returns Validation result with error messages
|
|
222
|
+
*/
|
|
223
|
+
export function validateFilter(filter) {
|
|
224
|
+
const errors = [];
|
|
225
|
+
if (!filter.type) {
|
|
226
|
+
errors.push('Filter must have a type');
|
|
227
|
+
return { valid: false, errors };
|
|
228
|
+
}
|
|
229
|
+
const validTypes = ['AND', 'OR', 'NOT', 'FIELD'];
|
|
230
|
+
if (!validTypes.includes(filter.type)) {
|
|
231
|
+
errors.push(`Invalid filter type: ${filter.type}. Must be one of: ${validTypes.join(', ')}`);
|
|
232
|
+
}
|
|
233
|
+
if (filter.type === 'AND' || filter.type === 'OR') {
|
|
234
|
+
if (!filter.children || !Array.isArray(filter.children)) {
|
|
235
|
+
errors.push(`${filter.type} filter must have a 'children' array`);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
for (let i = 0; i < filter.children.length; i++) {
|
|
239
|
+
const childResult = validateFilter(filter.children[i]);
|
|
240
|
+
if (!childResult.valid) {
|
|
241
|
+
errors.push(...childResult.errors.map((e) => `${filter.type}.children[${i}]: ${e}`));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (filter.type === 'NOT') {
|
|
247
|
+
if (!filter.child) {
|
|
248
|
+
errors.push("NOT filter must have a 'child' expression");
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const childResult = validateFilter(filter.child);
|
|
252
|
+
if (!childResult.valid) {
|
|
253
|
+
errors.push(...childResult.errors.map((e) => `NOT.child: ${e}`));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (filter.type === 'FIELD') {
|
|
258
|
+
if (!filter.field) {
|
|
259
|
+
errors.push("FIELD filter must have a 'field' property");
|
|
260
|
+
}
|
|
261
|
+
if (!filter.operator) {
|
|
262
|
+
errors.push("FIELD filter must have an 'operator' property");
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
const validOps = [
|
|
266
|
+
'eq', 'gt', 'lt', 'gte', 'lte', 'in', 'contains', 'between',
|
|
267
|
+
];
|
|
268
|
+
if (!validOps.includes(filter.operator)) {
|
|
269
|
+
errors.push(`Invalid operator: ${filter.operator}. Must be one of: ${validOps.join(', ')}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return { valid: errors.length === 0, errors };
|
|
274
|
+
}
|
|
275
|
+
// ============================================================================
|
|
276
|
+
// Reset (for testing)
|
|
277
|
+
// ============================================================================
|
|
278
|
+
/**
|
|
279
|
+
* Reset native engine state (for testing only)
|
|
280
|
+
*/
|
|
281
|
+
export function resetNativeEngine() {
|
|
282
|
+
nativeEngine = null;
|
|
283
|
+
nativeChecked = false;
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=filter-adapter.js.map
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module integrations/ruvector/gnn-wrapper
|
|
9
9
|
*/
|
|
10
10
|
import type { IEmbedding, IHNSWConfig, EmbeddingNamespace, ISearchOptions } from '../embeddings/base/types';
|
|
11
|
+
import type { DitheredResult, DitherOptions } from './dither-adapter.js';
|
|
11
12
|
/** Check if native GNN module is available */
|
|
12
13
|
export declare function isGNNAvailable(): boolean;
|
|
13
14
|
/** CompressionLevelConfig type compatible with @ruvector/gnn */
|
|
@@ -239,6 +240,25 @@ export declare class TensorCompressionFactory {
|
|
|
239
240
|
* Get compression level for frequency
|
|
240
241
|
*/
|
|
241
242
|
static getLevel(accessFreq: number): QECompressionLevel;
|
|
243
|
+
/**
|
|
244
|
+
* Compress tensor with optional deterministic dithering (Task 1.4)
|
|
245
|
+
*
|
|
246
|
+
* When the `useDeterministicDither` feature flag is enabled, applies
|
|
247
|
+
* golden-ratio dithered quantization as a post-processing step before
|
|
248
|
+
* passing to the native compressor. This improves reconstruction quality
|
|
249
|
+
* at low bit depths while guaranteeing cross-platform reproducibility.
|
|
250
|
+
*
|
|
251
|
+
* When the flag is disabled, behaves identically to `compressWithLevel`.
|
|
252
|
+
*
|
|
253
|
+
* @param embedding - Raw embedding vector
|
|
254
|
+
* @param level - Compression level
|
|
255
|
+
* @param ditherOptions - Optional dither configuration (seed, bitDepth override)
|
|
256
|
+
* @returns Compressed data string, optionally with dither metadata
|
|
257
|
+
*/
|
|
258
|
+
static compressWithDither(embedding: number[] | Float32Array, level: QECompressionLevel, ditherOptions?: Partial<DitherOptions>): {
|
|
259
|
+
compressed: string;
|
|
260
|
+
ditherResult?: DitheredResult;
|
|
261
|
+
};
|
|
242
262
|
}
|
|
243
263
|
export declare function getRuvectorLayer(): any;
|
|
244
264
|
export declare function getTensorCompress(): any;
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* @module integrations/ruvector/gnn-wrapper
|
|
9
9
|
*/
|
|
10
10
|
import { safeJsonParse } from '../../shared/safe-json.js';
|
|
11
|
+
import { isDeterministicDitherEnabled } from './feature-flags.js';
|
|
12
|
+
import { applyDither } from './dither-adapter.js';
|
|
11
13
|
// Lazy-load @ruvector/gnn native bindings (may not be available on all platforms)
|
|
12
14
|
// Follows the pattern in kernel/unified-memory-hnsw.ts (lines 22-35)
|
|
13
15
|
let _RuvectorLayer = null;
|
|
@@ -483,6 +485,44 @@ export class TensorCompressionFactory {
|
|
|
483
485
|
requireGNN();
|
|
484
486
|
return _getCompressionLevel(accessFreq);
|
|
485
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* Compress tensor with optional deterministic dithering (Task 1.4)
|
|
490
|
+
*
|
|
491
|
+
* When the `useDeterministicDither` feature flag is enabled, applies
|
|
492
|
+
* golden-ratio dithered quantization as a post-processing step before
|
|
493
|
+
* passing to the native compressor. This improves reconstruction quality
|
|
494
|
+
* at low bit depths while guaranteeing cross-platform reproducibility.
|
|
495
|
+
*
|
|
496
|
+
* When the flag is disabled, behaves identically to `compressWithLevel`.
|
|
497
|
+
*
|
|
498
|
+
* @param embedding - Raw embedding vector
|
|
499
|
+
* @param level - Compression level
|
|
500
|
+
* @param ditherOptions - Optional dither configuration (seed, bitDepth override)
|
|
501
|
+
* @returns Compressed data string, optionally with dither metadata
|
|
502
|
+
*/
|
|
503
|
+
static compressWithDither(embedding, level, ditherOptions) {
|
|
504
|
+
const embeddingFloat32 = embedding instanceof Float32Array
|
|
505
|
+
? embedding
|
|
506
|
+
: new Float32Array(embedding);
|
|
507
|
+
// Apply dithering if feature flag is enabled
|
|
508
|
+
let ditherResult;
|
|
509
|
+
if (isDeterministicDitherEnabled()) {
|
|
510
|
+
// Map compression level to a default bit depth
|
|
511
|
+
const bitDepthMap = {
|
|
512
|
+
'none': 32,
|
|
513
|
+
'half': 16,
|
|
514
|
+
'pq8': 8,
|
|
515
|
+
'pq4': 4,
|
|
516
|
+
'binary': 1,
|
|
517
|
+
};
|
|
518
|
+
const bitDepth = ditherOptions?.bitDepth ?? bitDepthMap[level] ?? 8;
|
|
519
|
+
const seed = ditherOptions?.seed ?? 0;
|
|
520
|
+
ditherResult = applyDither(embeddingFloat32, bitDepth, seed);
|
|
521
|
+
}
|
|
522
|
+
// Compress using the standard path (native compressor)
|
|
523
|
+
const compressed = this.compressWithLevel(ditherResult ? ditherResult.dequantized : embedding, level);
|
|
524
|
+
return { compressed, ditherResult };
|
|
525
|
+
}
|
|
486
526
|
}
|
|
487
527
|
// ============================================================================
|
|
488
528
|
// Re-exports from @ruvector/gnn for advanced users
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HNSW Health Monitor - Spectral Health Monitoring
|
|
3
|
+
*
|
|
4
|
+
* Monitors the structural health of HNSW indexes using spectral graph
|
|
5
|
+
* theory metrics. Computes Fiedler value (algebraic connectivity),
|
|
6
|
+
* spectral gap, effective resistance, and a combined coherence score.
|
|
7
|
+
*
|
|
8
|
+
* Uses TypeScript power iteration and sample-based estimation for
|
|
9
|
+
* spectral computation. No native package exists for this — the
|
|
10
|
+
* TypeScript implementation is the production implementation.
|
|
11
|
+
*
|
|
12
|
+
* @see Task 3.4: HNSW Health Monitoring
|
|
13
|
+
* @module integrations/ruvector/hnsw-health-monitor
|
|
14
|
+
*/
|
|
15
|
+
import type { IHnswIndexProvider } from '../../kernel/hnsw-index-provider.js';
|
|
16
|
+
export { buildLaplacian, laplacianMultiply, vectorNorm, normalizeInPlace, deflateVector, approximateFiedlerValue, approximateSpectralGap, estimateEffectiveResistance, computeCoherenceScore, } from './spectral-math.js';
|
|
17
|
+
/**
|
|
18
|
+
* Alert types generated when health metrics exceed thresholds.
|
|
19
|
+
*/
|
|
20
|
+
export type HealthAlertType = 'FragileIndex' | 'PoorExpansion' | 'HighResistance' | 'LowCoherence';
|
|
21
|
+
/**
|
|
22
|
+
* A single health alert with context.
|
|
23
|
+
*/
|
|
24
|
+
export interface HealthAlert {
|
|
25
|
+
/** Alert classification */
|
|
26
|
+
readonly type: HealthAlertType;
|
|
27
|
+
/** Human-readable description */
|
|
28
|
+
readonly message: string;
|
|
29
|
+
/** The metric value that triggered the alert */
|
|
30
|
+
readonly value: number;
|
|
31
|
+
/** The threshold that was exceeded */
|
|
32
|
+
readonly threshold: number;
|
|
33
|
+
/** When the alert was generated */
|
|
34
|
+
readonly timestamp: Date;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Spectral health metrics for an HNSW index.
|
|
38
|
+
*/
|
|
39
|
+
export interface SpectralMetrics {
|
|
40
|
+
/** Second smallest eigenvalue of graph Laplacian (algebraic connectivity) */
|
|
41
|
+
readonly fiedlerValue: number;
|
|
42
|
+
/** Difference between 2nd and 1st eigenvalues (expansion quality) */
|
|
43
|
+
readonly spectralGap: number;
|
|
44
|
+
/** Average resistance distance between sampled node pairs */
|
|
45
|
+
readonly effectiveResistance: number;
|
|
46
|
+
/** Combined health metric (0-1, higher = healthier) */
|
|
47
|
+
readonly coherenceScore: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Full health report for an HNSW index.
|
|
51
|
+
*/
|
|
52
|
+
export interface HnswHealthReport {
|
|
53
|
+
/** Whether the index is considered healthy */
|
|
54
|
+
readonly healthy: boolean;
|
|
55
|
+
/** Spectral health metrics */
|
|
56
|
+
readonly metrics: SpectralMetrics;
|
|
57
|
+
/** Active alerts (empty if healthy) */
|
|
58
|
+
readonly alerts: HealthAlert[];
|
|
59
|
+
/** Number of vectors in the index */
|
|
60
|
+
readonly indexSize: number;
|
|
61
|
+
/** Whether native ruvector-coherence was used */
|
|
62
|
+
readonly usedNativeBackend: boolean;
|
|
63
|
+
/** Whether adjacency was built from actual search or circular approximation */
|
|
64
|
+
readonly adjacencySource: 'actual-search' | 'approximate';
|
|
65
|
+
/** Time taken for the health check in ms */
|
|
66
|
+
readonly checkDurationMs: number;
|
|
67
|
+
/** When the check was performed */
|
|
68
|
+
readonly checkedAt: Date;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A timestamped health metric point for history tracking.
|
|
72
|
+
*/
|
|
73
|
+
export interface HealthMetricPoint {
|
|
74
|
+
/** Coherence score at this point */
|
|
75
|
+
readonly coherenceScore: number;
|
|
76
|
+
/** Fiedler value at this point */
|
|
77
|
+
readonly fiedlerValue: number;
|
|
78
|
+
/** Index size at measurement time */
|
|
79
|
+
readonly indexSize: number;
|
|
80
|
+
/** Whether the index was healthy */
|
|
81
|
+
readonly healthy: boolean;
|
|
82
|
+
/** Timestamp of measurement */
|
|
83
|
+
readonly timestamp: Date;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Configuration for the HNSW health monitor.
|
|
87
|
+
*/
|
|
88
|
+
export interface HnswHealthMonitorConfig {
|
|
89
|
+
/** Fiedler value threshold for FragileIndex alert (default: 0.01) */
|
|
90
|
+
readonly fiedlerThreshold: number;
|
|
91
|
+
/** Spectral gap threshold for PoorExpansion alert (default: 0.1) */
|
|
92
|
+
readonly spectralGapThreshold: number;
|
|
93
|
+
/** Effective resistance threshold for HighResistance alert (default: 10.0) */
|
|
94
|
+
readonly resistanceThreshold: number;
|
|
95
|
+
/** Coherence score threshold for LowCoherence alert (default: 0.3) */
|
|
96
|
+
readonly coherenceThreshold: number;
|
|
97
|
+
/** Maximum iterations for power iteration (default: 100) */
|
|
98
|
+
readonly maxPowerIterations: number;
|
|
99
|
+
/** Convergence tolerance for power iteration (default: 1e-6) */
|
|
100
|
+
readonly convergenceTolerance: number;
|
|
101
|
+
/** Number of node pairs to sample for resistance estimation (default: 50) */
|
|
102
|
+
readonly resistanceSampleSize: number;
|
|
103
|
+
/** Maximum history entries to retain (default: 200) */
|
|
104
|
+
readonly maxHistoryEntries: number;
|
|
105
|
+
/** Minimum index size for meaningful spectral analysis (default: 3) */
|
|
106
|
+
readonly minIndexSize: number;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Default health monitor configuration.
|
|
110
|
+
*/
|
|
111
|
+
export declare const DEFAULT_HNSW_HEALTH_CONFIG: HnswHealthMonitorConfig;
|
|
112
|
+
export declare const ALERT_THRESHOLDS: {
|
|
113
|
+
readonly FragileIndex: 0.01;
|
|
114
|
+
readonly PoorExpansion: 0.1;
|
|
115
|
+
readonly HighResistance: 10;
|
|
116
|
+
readonly LowCoherence: 0.3;
|
|
117
|
+
};
|
|
118
|
+
/** Reset native loader state (for testing) */
|
|
119
|
+
export declare function _resetNativeLoader(): void;
|
|
120
|
+
/**
|
|
121
|
+
* Result from building adjacency from an HNSW index.
|
|
122
|
+
*/
|
|
123
|
+
export interface AdjacencyResult {
|
|
124
|
+
/** Adjacency list for the graph */
|
|
125
|
+
adjacency: number[][];
|
|
126
|
+
/** Number of nodes in the graph */
|
|
127
|
+
nodeCount: number;
|
|
128
|
+
/** Whether adjacency was built from actual search or circular approximation */
|
|
129
|
+
adjacencySource: 'actual-search' | 'approximate';
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Build an adjacency list from an HNSW-like index by using similarity
|
|
133
|
+
* between stored vectors. When stored vectors are provided, each vector
|
|
134
|
+
* is searched against the index to discover real neighbors. Otherwise,
|
|
135
|
+
* falls back to a circular approximation.
|
|
136
|
+
*
|
|
137
|
+
* @param index - The HNSW index provider
|
|
138
|
+
* @param maxNeighbors - Maximum neighbors per node to consider
|
|
139
|
+
* @param storedVectors - Optional map of id to vector for real neighbor discovery
|
|
140
|
+
* @returns Adjacency list, node count, and adjacency source
|
|
141
|
+
*/
|
|
142
|
+
export declare function buildAdjacencyFromIndex(index: IHnswIndexProvider, maxNeighbors?: number, storedVectors?: Map<number, Float32Array>): AdjacencyResult;
|
|
143
|
+
/**
|
|
144
|
+
* HNSW Health Monitor
|
|
145
|
+
*
|
|
146
|
+
* Monitors the spectral health of HNSW indexes by computing graph-theoretic
|
|
147
|
+
* metrics. Uses ruvector-coherence for native computation when available,
|
|
148
|
+
* falling back to TypeScript approximations.
|
|
149
|
+
*
|
|
150
|
+
* Health checks are designed to be lightweight enough for periodic use
|
|
151
|
+
* without blocking search operations.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const monitor = new HnswHealthMonitor();
|
|
156
|
+
* const report = monitor.checkHealth(hnswIndex);
|
|
157
|
+
* if (!report.healthy) {
|
|
158
|
+
* console.warn('HNSW index unhealthy:', report.alerts);
|
|
159
|
+
* }
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export declare class HnswHealthMonitor {
|
|
163
|
+
private readonly config;
|
|
164
|
+
private readonly alerts;
|
|
165
|
+
private readonly history;
|
|
166
|
+
private lastReport;
|
|
167
|
+
private useNative;
|
|
168
|
+
private nativeChecked;
|
|
169
|
+
constructor(config?: Partial<HnswHealthMonitorConfig>);
|
|
170
|
+
/**
|
|
171
|
+
* Perform a health check on an HNSW index.
|
|
172
|
+
*
|
|
173
|
+
* Computes spectral metrics and generates alerts if thresholds are
|
|
174
|
+
* exceeded. Results are recorded in the metrics history.
|
|
175
|
+
*
|
|
176
|
+
* @param index - The HNSW index to check
|
|
177
|
+
* @returns Health report with metrics and alerts
|
|
178
|
+
*/
|
|
179
|
+
checkHealth(index: IHnswIndexProvider): HnswHealthReport;
|
|
180
|
+
/**
|
|
181
|
+
* Get current active alerts.
|
|
182
|
+
*
|
|
183
|
+
* @returns Array of active health alerts
|
|
184
|
+
*/
|
|
185
|
+
getAlerts(): HealthAlert[];
|
|
186
|
+
/**
|
|
187
|
+
* Get health metrics history.
|
|
188
|
+
*
|
|
189
|
+
* @param limit - Maximum entries to return (most recent)
|
|
190
|
+
* @returns Array of metric points, most recent last
|
|
191
|
+
*/
|
|
192
|
+
getMetricsHistory(limit?: number): HealthMetricPoint[];
|
|
193
|
+
/**
|
|
194
|
+
* Check whether the index is currently healthy (no alerts).
|
|
195
|
+
*
|
|
196
|
+
* If no health check has been performed yet, returns true (optimistic).
|
|
197
|
+
*
|
|
198
|
+
* @returns true if the last health check found no alerts
|
|
199
|
+
*/
|
|
200
|
+
isHealthy(): boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Get the last health report, or null if no check has been performed.
|
|
203
|
+
*/
|
|
204
|
+
getLastReport(): HnswHealthReport | null;
|
|
205
|
+
/**
|
|
206
|
+
* Clear all history and alerts.
|
|
207
|
+
*/
|
|
208
|
+
clearHistory(): void;
|
|
209
|
+
/**
|
|
210
|
+
* Compute metrics using the native ruvector-coherence module.
|
|
211
|
+
*/
|
|
212
|
+
private computeNativeMetrics;
|
|
213
|
+
/**
|
|
214
|
+
* Compute metrics using TypeScript approximations.
|
|
215
|
+
*/
|
|
216
|
+
private computeApproximateMetrics;
|
|
217
|
+
/**
|
|
218
|
+
* Generate alerts based on metric thresholds.
|
|
219
|
+
*/
|
|
220
|
+
private generateAlerts;
|
|
221
|
+
/**
|
|
222
|
+
* Create a report for indexes too small for spectral analysis.
|
|
223
|
+
*/
|
|
224
|
+
private createSmallIndexReport;
|
|
225
|
+
/**
|
|
226
|
+
* Add a history point, trimming if over limit.
|
|
227
|
+
*/
|
|
228
|
+
private addHistoryPoint;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Create an HnswHealthMonitor instance.
|
|
232
|
+
*
|
|
233
|
+
* @param config - Optional configuration overrides
|
|
234
|
+
* @returns A new HnswHealthMonitor
|
|
235
|
+
*/
|
|
236
|
+
export declare function createHnswHealthMonitor(config?: Partial<HnswHealthMonitorConfig>): HnswHealthMonitor;
|
|
237
|
+
//# sourceMappingURL=hnsw-health-monitor.d.ts.map
|