claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
904 lines • 40.3 kB
JavaScript
/**
* Embeddings MCP Tools for CLI
*
* Tool definitions for ONNX embeddings with hyperbolic support and neural substrate.
* Implements ADR-024: Embeddings MCP Tools
*/
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { join, resolve } from 'path';
import { validateIdentifier, validateText } from './validate-input.js';
// Configuration paths
const CONFIG_DIR = '.claude-flow';
const EMBEDDINGS_CONFIG = 'embeddings.json';
const MODELS_DIR = 'models';
function getConfigPath() {
return resolve(join(CONFIG_DIR, EMBEDDINGS_CONFIG));
}
function ensureConfigDir() {
const dir = resolve(CONFIG_DIR);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
function loadConfig() {
try {
const path = getConfigPath();
if (existsSync(path)) {
return JSON.parse(readFileSync(path, 'utf-8'));
}
}
catch {
// Return null on error
}
return null;
}
function saveConfig(config) {
ensureConfigDir();
writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), 'utf-8');
}
// Real ONNX embedding generation via memory-initializer
let realEmbeddingFn = null;
async function getRealEmbeddingFunction() {
if (!realEmbeddingFn) {
try {
const { generateEmbedding } = await import('../memory/memory-initializer.js');
realEmbeddingFn = generateEmbedding;
}
catch {
realEmbeddingFn = null;
}
}
return realEmbeddingFn;
}
// Generate real ONNX embedding (falls back to deterministic hash if ONNX unavailable)
async function generateRealEmbedding(text, dimension) {
const realFn = await getRealEmbeddingFunction();
if (realFn) {
try {
const result = await realFn(text);
return result.embedding;
}
catch {
// Fall through to fallback
}
}
// Fallback: deterministic hash-based (only if ONNX truly unavailable)
console.warn('[MCP] ONNX unavailable, using fallback embedding');
const embedding = [];
let hash = 0;
for (let i = 0; i < text.length; i++) {
hash = ((hash << 5) - hash + text.charCodeAt(i)) | 0;
}
for (let i = 0; i < dimension; i++) {
const seed = hash + i * 1337;
embedding.push(Math.sin(seed) * Math.cos(seed * 0.5));
}
// L2 normalize
const norm = Math.sqrt(embedding.reduce((sum, x) => sum + x * x, 0));
return embedding.map(x => x / norm);
}
// Convert Euclidean embedding to Poincaré ball
function toPoincare(euclidean, curvature) {
const c = Math.abs(curvature);
const sqrtC = Math.sqrt(c);
const norm = Math.sqrt(euclidean.reduce((sum, x) => sum + x * x, 0));
// Exponential map at origin
const factor = Math.tanh(sqrtC * norm / 2) / (sqrtC * norm + 1e-15);
return euclidean.map(x => x * factor);
}
// Poincaré distance
function poincareDistance(a, b, curvature) {
const c = Math.abs(curvature);
const diffSq = a.reduce((sum, _, i) => sum + (a[i] - b[i]) ** 2, 0);
const normASq = a.reduce((sum, x) => sum + x * x, 0);
const normBSq = b.reduce((sum, x) => sum + x * x, 0);
const denom = (1 - normASq) * (1 - normBSq);
const delta = 2 * diffSq / (denom + 1e-15);
return (1 / Math.sqrt(c)) * Math.acosh(1 + delta);
}
// Cosine similarity
function cosineSimilarity(a, b) {
const dot = a.reduce((sum, _, i) => sum + a[i] * b[i], 0);
const normA = Math.sqrt(a.reduce((sum, x) => sum + x * x, 0));
const normB = Math.sqrt(b.reduce((sum, x) => sum + x * x, 0));
return dot / (normA * normB + 1e-15);
}
export const embeddingsTools = [
{
name: 'embeddings_init',
description: 'Initialize the ONNX embedding subsystem with hyperbolic support Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {
model: {
type: 'string',
description: 'ONNX model ID',
enum: ['Xenova/all-MiniLM-L6-v2', 'Xenova/all-mpnet-base-v2'],
default: 'Xenova/all-MiniLM-L6-v2',
},
hyperbolic: {
type: 'boolean',
description: 'Enable hyperbolic (Poincaré ball) embeddings',
default: true,
},
curvature: {
type: 'number',
description: 'Poincaré ball curvature (negative)',
default: -1,
},
cacheSize: {
type: 'number',
description: 'LRU cache size',
default: 256,
},
force: {
type: 'boolean',
description: 'Overwrite existing configuration',
default: false,
},
},
},
handler: async (input) => {
const model = input.model || 'Xenova/all-MiniLM-L6-v2';
const hyperbolic = input.hyperbolic !== false;
const curvature = input.curvature || -1;
const cacheSize = input.cacheSize || 256;
const force = input.force === true;
const existingConfig = loadConfig();
if (existingConfig && !force) {
return {
success: false,
error: 'Embeddings already initialized. Use force=true to overwrite.',
existingConfig: {
model: existingConfig.model,
initialized: existingConfig.initialized,
},
};
}
const dimension = model.includes('mpnet') ? 768 : 384;
const modelPath = resolve(join(CONFIG_DIR, MODELS_DIR));
// Create models directory
if (!existsSync(modelPath)) {
mkdirSync(modelPath, { recursive: true });
}
const config = {
model,
modelPath,
dimension,
cacheSize,
hyperbolic: {
enabled: hyperbolic,
curvature,
epsilon: 1e-15,
maxNorm: 1 - 1e-5,
},
neural: {
enabled: true,
driftThreshold: 0.3,
decayRate: 0.01,
},
initialized: new Date().toISOString(),
};
saveConfig(config);
return {
success: true,
config: {
model,
dimension,
cacheSize,
hyperbolic: hyperbolic ? { enabled: true, curvature } : { enabled: false },
neural: { enabled: true },
},
paths: {
config: getConfigPath(),
models: modelPath,
},
message: 'Embedding subsystem initialized successfully',
};
},
},
{
name: 'embeddings_generate',
description: 'Generate embeddings for text (Euclidean or hyperbolic) Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {
text: {
type: 'string',
description: 'Text to embed',
},
hyperbolic: {
type: 'boolean',
description: 'Return hyperbolic (Poincaré) embedding',
default: false,
},
normalize: {
type: 'boolean',
description: 'L2 normalize the embedding',
default: true,
},
},
required: ['text'],
},
handler: async (input) => {
const config = loadConfig();
if (!config) {
return {
success: false,
error: 'Embeddings not initialized. Run embeddings/init first.',
};
}
const text = input.text;
{
const v = validateText(text, 'text');
if (!v.valid)
return { success: false, error: v.error };
}
const useHyperbolic = input.hyperbolic === true && config.hyperbolic.enabled;
// Generate real ONNX embedding
const embedding = await generateRealEmbedding(text, config.dimension);
let result;
let geometry;
if (useHyperbolic) {
result = toPoincare(embedding, config.hyperbolic.curvature);
geometry = 'poincare';
}
else {
result = embedding;
geometry = 'euclidean';
}
return {
success: true,
embedding: result,
metadata: {
model: config.model,
dimension: config.dimension,
geometry,
curvature: useHyperbolic ? config.hyperbolic.curvature : null,
textLength: text.length,
norm: Math.sqrt(result.reduce((sum, x) => sum + x * x, 0)),
},
};
},
},
{
name: 'embeddings_compare',
description: 'Compare similarity between two texts Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {
text1: {
type: 'string',
description: 'First text',
},
text2: {
type: 'string',
description: 'Second text',
},
metric: {
type: 'string',
description: 'Similarity metric',
enum: ['cosine', 'euclidean', 'poincare'],
default: 'cosine',
},
},
required: ['text1', 'text2'],
},
handler: async (input) => {
const config = loadConfig();
if (!config) {
return {
success: false,
error: 'Embeddings not initialized. Run embeddings/init first.',
};
}
const text1 = input.text1;
const text2 = input.text2;
const metric = input.metric || 'cosine';
{
const v = validateText(text1, 'text1');
if (!v.valid)
return { success: false, error: v.error };
}
{
const v = validateText(text2, 'text2');
if (!v.valid)
return { success: false, error: v.error };
}
// Generate real ONNX embeddings for both texts
const [emb1, emb2] = await Promise.all([
generateRealEmbedding(text1, config.dimension),
generateRealEmbedding(text2, config.dimension)
]);
let similarity;
let distance;
switch (metric) {
case 'poincare':
if (!config.hyperbolic.enabled) {
return {
success: false,
error: 'Hyperbolic mode not enabled. Initialize with hyperbolic=true.',
};
}
const poinc1 = toPoincare(emb1, config.hyperbolic.curvature);
const poinc2 = toPoincare(emb2, config.hyperbolic.curvature);
distance = poincareDistance(poinc1, poinc2, config.hyperbolic.curvature);
similarity = 1 / (1 + distance);
break;
case 'euclidean':
distance = Math.sqrt(emb1.reduce((sum, _, i) => sum + (emb1[i] - emb2[i]) ** 2, 0));
similarity = 1 / (1 + distance);
break;
default: // cosine
similarity = cosineSimilarity(emb1, emb2);
distance = 1 - similarity;
}
return {
success: true,
similarity,
distance,
metric,
texts: {
text1: { length: text1.length, preview: text1.slice(0, 50) },
text2: { length: text2.length, preview: text2.slice(0, 50) },
},
interpretation: similarity > 0.8 ? 'very similar' :
similarity > 0.6 ? 'similar' :
similarity > 0.4 ? 'somewhat similar' :
similarity > 0.2 ? 'different' : 'very different',
};
},
},
{
name: 'embeddings_search',
description: 'Semantic search across stored embeddings Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query',
},
topK: {
type: 'number',
description: 'Number of results to return',
default: 5,
},
threshold: {
type: 'number',
description: 'Minimum similarity threshold (0-1)',
default: 0.5,
},
namespace: {
type: 'string',
description: 'Search in specific namespace',
},
},
required: ['query'],
},
handler: async (input) => {
const config = loadConfig();
if (!config) {
return {
success: false,
error: 'Embeddings not initialized. Run embeddings/init first.',
};
}
const query = input.query;
const topK = input.topK || 5;
const threshold = input.threshold || 0.5;
const namespace = input.namespace;
{
const v = validateText(query, 'query');
if (!v.valid)
return { success: false, error: v.error };
}
if (namespace) {
const v = validateIdentifier(namespace, 'namespace');
if (!v.valid)
return { success: false, error: v.error };
}
const startTime = performance.now();
// Generate real ONNX embedding for query
const queryEmbedding = await generateRealEmbedding(query, config.dimension);
// Try to search using real memory search
try {
const { searchEntries } = await import('../memory/memory-initializer.js');
const searchResult = await searchEntries({
query,
limit: topK,
threshold,
namespace: namespace || 'all'
});
const searchTime = (performance.now() - startTime).toFixed(2);
return {
success: true,
query,
results: searchResult.results.map((r) => ({
key: r.key,
content: r.content?.substring(0, 100),
similarity: r.score,
namespace: r.namespace
})),
metadata: {
model: config.model,
topK,
threshold,
namespace: namespace || 'all',
searchTime: `${searchTime}ms`,
indexType: config.hyperbolic.enabled ? 'HNSW (hyperbolic)' : 'HNSW (euclidean)',
resultCount: searchResult.results.length
},
};
}
catch {
// Database not available - return empty but truthful
const searchTime = (performance.now() - startTime).toFixed(2);
return {
success: true,
query,
results: [],
metadata: {
model: config.model,
topK,
threshold,
namespace: namespace || 'all',
searchTime: `${searchTime}ms`,
indexType: config.hyperbolic.enabled ? 'HNSW (hyperbolic)' : 'HNSW (euclidean)',
},
message: 'No embeddings indexed yet. Use memory store to add documents.',
};
}
},
},
{
name: 'embeddings_neural',
description: 'Neural substrate operations (RuVector integration) Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
description: 'Neural action',
enum: ['status', 'init', 'drift', 'consolidate', 'adapt'],
default: 'status',
},
driftThreshold: {
type: 'number',
description: 'Semantic drift detection threshold',
default: 0.3,
},
decayRate: {
type: 'number',
description: 'Memory decay rate (hippocampal dynamics)',
default: 0.01,
},
},
},
handler: async (input) => {
const config = loadConfig();
if (!config) {
return {
success: false,
error: 'Embeddings not initialized. Run embeddings/init first.',
};
}
const action = input.action || 'status';
switch (action) {
case 'init':
config.neural = {
enabled: true,
driftThreshold: input.driftThreshold || 0.3,
decayRate: input.decayRate || 0.01,
ruvector: {
enabled: true,
sona: true,
flashAttention: true,
ewcPlusPlus: true,
},
features: {
semanticDrift: true,
memoryPhysics: true,
stateMachine: true,
swarmCoordination: true,
coherenceMonitor: true,
},
};
saveConfig(config);
return {
success: true,
action: 'init',
neural: config.neural,
message: 'Neural substrate initialized with RuVector integration',
};
case 'drift':
// Get real drift metrics if available
try {
const { getIntelligenceStats } = await import('../memory/intelligence.js');
const stats = getIntelligenceStats();
return {
success: true,
action: 'drift',
status: {
semanticDrift: {
enabled: config.neural.features?.semanticDrift ?? false,
threshold: config.neural.driftThreshold,
patternsTracked: stats.patternsLearned,
status: stats.patternsLearned > 0 ? 'tracking' : 'no patterns',
},
},
message: stats.patternsLearned > 0
? `Tracking ${stats.patternsLearned} patterns for drift`
: 'No patterns stored yet - drift detection inactive',
};
}
catch {
return {
success: true,
action: 'drift',
status: { semanticDrift: { enabled: false, reason: 'Intelligence module unavailable' } },
};
}
case 'consolidate':
// Get real consolidation metrics
try {
const { getIntelligenceStats } = await import('../memory/intelligence.js');
const stats = getIntelligenceStats();
return {
success: true,
action: 'consolidate',
status: {
memoryPhysics: {
enabled: config.neural.features?.memoryPhysics ?? false,
decayRate: config.neural.decayRate,
patternsStored: stats.reasoningBankSize,
trajectoriesRecorded: stats.trajectoriesRecorded,
},
},
message: `ReasoningBank: ${stats.reasoningBankSize} patterns, ${stats.trajectoriesRecorded} trajectories`,
};
}
catch {
return {
success: true,
action: 'consolidate',
status: { memoryPhysics: { enabled: false, reason: 'Intelligence module unavailable' } },
};
}
case 'adapt':
// Get real SONA adaptation metrics
try {
const { benchmarkAdaptation, initializeIntelligence } = await import('../memory/intelligence.js');
await initializeIntelligence();
const benchmark = benchmarkAdaptation(100);
return {
success: true,
action: 'adapt',
status: {
sona: {
enabled: true,
adaptationTime: `${(benchmark.avgMs * 1000).toFixed(2)}μs`,
targetMet: benchmark.targetMet,
minTime: `${(benchmark.minMs * 1000).toFixed(2)}μs`,
maxTime: `${(benchmark.maxMs * 1000).toFixed(2)}μs`,
},
},
message: benchmark.targetMet
? `SONA adaptation: ${(benchmark.avgMs * 1000).toFixed(2)}μs (target <50μs met)`
: `SONA adaptation: ${(benchmark.avgMs * 1000).toFixed(2)}μs (target not met)`,
};
}
catch {
return {
success: true,
action: 'adapt',
status: { sona: { enabled: false, reason: 'Intelligence module unavailable' } },
};
}
default: // status
// Get real neural system status
try {
const { getIntelligenceStats, benchmarkAdaptation, initializeIntelligence } = await import('../memory/intelligence.js');
await initializeIntelligence();
const stats = getIntelligenceStats();
const benchmark = benchmarkAdaptation(50);
return {
success: true,
action: 'status',
neural: {
enabled: config.neural.enabled,
sonaEnabled: stats.sonaEnabled,
ruvector: config.neural.ruvector || { enabled: false },
features: config.neural.features || {},
realMetrics: {
patternsLearned: stats.patternsLearned,
trajectoriesRecorded: stats.trajectoriesRecorded,
reasoningBankSize: stats.reasoningBankSize,
adaptationTime: `${(benchmark.avgMs * 1000).toFixed(2)}μs`,
targetMet: benchmark.targetMet,
lastAdaptation: stats.lastAdaptation
? new Date(stats.lastAdaptation).toISOString()
: null,
},
},
capabilities: [
stats.sonaEnabled ? '✅ SONA Active' : '❌ SONA Inactive',
benchmark.targetMet ? '✅ <0.05ms Target Met' : '⚠️ Target Not Met',
`${stats.patternsLearned} patterns learned`,
`${stats.trajectoriesRecorded} trajectories recorded`,
],
};
}
catch {
return {
success: true,
action: 'status',
neural: {
enabled: config.neural.enabled,
ruvector: config.neural.ruvector || { enabled: false },
features: config.neural.features || {},
},
message: 'Intelligence module not available - showing config only',
};
}
}
},
},
{
name: 'embeddings_hyperbolic',
description: 'Hyperbolic embedding operations (Poincaré ball) Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {
action: {
type: 'string',
description: 'Hyperbolic action',
enum: ['status', 'convert', 'distance', 'midpoint'],
default: 'status',
},
embedding: {
type: 'array',
description: 'Euclidean embedding to convert',
items: { type: 'number' },
},
embedding1: {
type: 'array',
description: 'First embedding for distance/midpoint',
items: { type: 'number' },
},
embedding2: {
type: 'array',
description: 'Second embedding for distance/midpoint',
items: { type: 'number' },
},
},
},
handler: async (input) => {
const config = loadConfig();
if (!config) {
return {
success: false,
error: 'Embeddings not initialized. Run embeddings/init first.',
};
}
if (!config.hyperbolic.enabled) {
return {
success: false,
error: 'Hyperbolic mode not enabled. Initialize with hyperbolic=true.',
};
}
const action = input.action || 'status';
const curvature = config.hyperbolic.curvature;
switch (action) {
case 'convert':
const embedding = input.embedding;
if (!embedding || !Array.isArray(embedding)) {
return { success: false, error: 'Embedding array required for convert action' };
}
const poincare = toPoincare(embedding, curvature);
return {
success: true,
action: 'convert',
euclidean: embedding,
poincare,
curvature,
poincareNorm: Math.sqrt(poincare.reduce((sum, x) => sum + x * x, 0)),
};
case 'distance':
const emb1 = input.embedding1;
const emb2 = input.embedding2;
if (!emb1 || !emb2) {
return { success: false, error: 'embedding1 and embedding2 required for distance action' };
}
const dist = poincareDistance(emb1, emb2, curvature);
return {
success: true,
action: 'distance',
distance: dist,
curvature,
interpretation: dist < 1 ? 'close' : dist < 2 ? 'moderate' : 'far',
};
case 'midpoint':
const e1 = input.embedding1;
const e2 = input.embedding2;
if (!e1 || !e2) {
return { success: false, error: 'embedding1 and embedding2 required for midpoint action' };
}
// Simplified midpoint (proper Möbius midpoint is more complex)
const mid = e1.map((_, i) => (e1[i] + e2[i]) / 2);
const norm = Math.sqrt(mid.reduce((sum, x) => sum + x * x, 0));
const scaledMid = mid.map(x => x * (config.hyperbolic.maxNorm / Math.max(norm, config.hyperbolic.maxNorm)));
return {
success: true,
action: 'midpoint',
midpoint: scaledMid,
curvature,
};
default: // status
return {
success: true,
action: 'status',
hyperbolic: {
enabled: true,
curvature,
epsilon: config.hyperbolic.epsilon,
maxNorm: config.hyperbolic.maxNorm,
},
benefits: [
'Better hierarchical data representation',
'Exponential capacity in low dimensions',
'Preserves tree-like structures',
'Natural for taxonomy embeddings',
],
};
}
},
},
{
name: 'embeddings_status',
description: 'Get embeddings system status and configuration Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {},
},
handler: async () => {
const config = loadConfig();
if (!config) {
return {
success: false,
initialized: false,
message: 'Embeddings not initialized. Run embeddings/init first.',
};
}
// ADR-093 F5: distinguish "@ruvector/core installed" from "wired into
// the embedding pipeline". Previously this collapsed both into a
// single `ruvector: boolean` field, which gave callers no way to
// tell whether re-running embeddings_init would help (#1698 partial
// regression on the MCP boundary).
let ruvectorAvailable = false;
let ruvectorVersion;
try {
const mod = await import('@ruvector/core');
ruvectorAvailable = !!mod;
try {
// Best-effort: many packages expose a `version` constant
ruvectorVersion = mod.version;
}
catch { /* ignore */ }
}
catch { /* not installed */ }
const ruvectorEnabled = config.neural.ruvector?.enabled ?? false;
return {
success: true,
initialized: true,
config: {
model: config.model,
dimension: config.dimension,
cacheSize: config.cacheSize,
hyperbolic: config.hyperbolic,
neural: {
enabled: config.neural.enabled,
// Backwards-compatible: keep the boolean view (truthy when wired).
ruvector: ruvectorEnabled,
// New shape — additive, non-breaking. Callers that need to
// distinguish "package is installed" from "feature wired in"
// read these instead of guessing from a single bool.
ruvectorStatus: {
available: ruvectorAvailable,
enabled: ruvectorEnabled,
version: ruvectorVersion,
},
},
},
paths: {
config: getConfigPath(),
models: config.modelPath,
},
initializedAt: config.initialized,
capabilities: {
onnxModels: ['Xenova/all-MiniLM-L6-v2', 'Xenova/all-mpnet-base-v2'],
geometries: ['euclidean', 'poincare'],
normalizations: ['L2', 'L1', 'minmax', 'zscore'],
features: ['semantic search', 'hyperbolic projection', 'neural substrate'],
},
};
},
},
// --- RaBitQ 1-bit quantized vector index ---
{
name: 'embeddings_rabitq_build',
description: 'Build RaBitQ 1-bit quantized index from stored embeddings (32× compression). Pre-filters candidates via Hamming scan before exact rerank. Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {
force: { type: 'boolean', description: 'Force rebuild even if index exists' },
},
},
handler: async (params) => {
const { buildRabitqIndex } = await import('../memory/rabitq-index.js');
return buildRabitqIndex({ force: params.force });
},
},
{
name: 'embeddings_rabitq_search',
description: 'Search via RaBitQ quantized index (fast Hamming scan). Returns candidate IDs for reranking. Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query text' },
k: { type: 'number', description: 'Number of results (default: 10)' },
namespace: { type: 'string', description: 'Filter by namespace' },
},
required: ['query'],
},
handler: async (params) => {
const { validateText: vt } = await import('./validate-input.js');
const v = vt(params.query, 'query');
if (!v.valid)
return { success: false, error: v.error };
const { searchRabitq } = await import('../memory/rabitq-index.js');
const { generateEmbedding } = await import('../memory/memory-initializer.js');
const queryEmb = await generateEmbedding(params.query);
const results = await searchRabitq(queryEmb.embedding, {
k: params.k || 10,
namespace: params.namespace,
});
if (!results) {
return { success: false, error: 'RaBitQ index not built. Call embeddings_rabitq_build first.' };
}
return {
success: true,
results: results.map(r => ({
id: r.id.substring(0, 12),
key: r.key,
namespace: r.namespace,
distance: Math.round(r.distance * 10000) / 10000,
})),
count: results.length,
};
},
},
{
name: 'embeddings_rabitq_status',
description: 'Get RaBitQ quantized index status — availability, vector count, compression ratio Use when text similarity matters beyond keyword match — native Grep finds exact strings, embeddings find meaning. Pair with memory_store / agentdb_pattern-search to land the vector against your knowledge base. For literal symbol search, native Grep is faster.',
category: 'embeddings',
inputSchema: {
type: 'object',
properties: {},
},
handler: async () => {
const { getRabitqStatus } = await import('../memory/rabitq-index.js');
return { success: true, ...getRabitqStatus() };
},
},
];
//# sourceMappingURL=embeddings-tools.js.map