245 lines
7.3 KiB
TypeScript
245 lines
7.3 KiB
TypeScript
/**
|
|
* HybridBackend - Combines SQLite (structured queries) + AgentDB (vector search)
|
|
*
|
|
* Per ADR-009: "HybridBackend (SQLite + AgentDB) as default"
|
|
* - SQLite for: Structured queries, ACID transactions, exact matches
|
|
* - AgentDB for: Semantic search, vector similarity, RAG
|
|
*
|
|
* @module v3/memory/hybrid-backend
|
|
*/
|
|
import { EventEmitter } from 'node:events';
|
|
import { IMemoryBackend, MemoryEntry, MemoryEntryUpdate, MemoryQuery, SearchOptions, SearchResult, BackendStats, HealthCheckResult, EmbeddingGenerator } from './types.js';
|
|
import { SQLiteBackend, SQLiteBackendConfig } from './sqlite-backend.js';
|
|
import { AgentDBBackend, AgentDBBackendConfig } from './agentdb-backend.js';
|
|
/**
|
|
* Configuration for HybridBackend
|
|
*/
|
|
export interface HybridBackendConfig {
|
|
/** SQLite configuration */
|
|
sqlite?: Partial<SQLiteBackendConfig>;
|
|
/** AgentDB configuration */
|
|
agentdb?: Partial<AgentDBBackendConfig>;
|
|
/** Default namespace */
|
|
defaultNamespace?: string;
|
|
/** Embedding generator function */
|
|
embeddingGenerator?: EmbeddingGenerator;
|
|
/** Query routing strategy */
|
|
routingStrategy?: 'auto' | 'sqlite-first' | 'agentdb-first';
|
|
/** Enable dual-write (write to both backends) */
|
|
dualWrite?: boolean;
|
|
/** Semantic search threshold for hybrid queries */
|
|
semanticThreshold?: number;
|
|
/** Maximum results to fetch from each backend in hybrid queries */
|
|
hybridMaxResults?: number;
|
|
}
|
|
/**
|
|
* Structured Query Interface
|
|
* Optimized for SQLite's strengths
|
|
*/
|
|
export interface StructuredQuery {
|
|
/** Exact key match */
|
|
key?: string;
|
|
/** Key prefix match */
|
|
keyPrefix?: string;
|
|
/** Namespace filter */
|
|
namespace?: string;
|
|
/** Owner filter */
|
|
ownerId?: string;
|
|
/** Type filter */
|
|
type?: string;
|
|
/** Time range filters */
|
|
createdAfter?: number;
|
|
createdBefore?: number;
|
|
updatedAfter?: number;
|
|
updatedBefore?: number;
|
|
/** Pagination */
|
|
limit?: number;
|
|
offset?: number;
|
|
}
|
|
/**
|
|
* Semantic Query Interface
|
|
* Optimized for AgentDB's vector search
|
|
*/
|
|
export interface SemanticQuery {
|
|
/** Content to search for (will be embedded) */
|
|
content?: string;
|
|
/** Pre-computed embedding */
|
|
embedding?: Float32Array;
|
|
/** Number of results */
|
|
k?: number;
|
|
/** Similarity threshold (0-1) */
|
|
threshold?: number;
|
|
/** Additional filters */
|
|
filters?: Partial<MemoryQuery>;
|
|
}
|
|
/**
|
|
* Hybrid Query Interface
|
|
* Combines structured + semantic search
|
|
*/
|
|
export interface HybridQuery {
|
|
/** Semantic component */
|
|
semantic: SemanticQuery;
|
|
/** Structured component */
|
|
structured?: StructuredQuery;
|
|
/** How to combine results */
|
|
combineStrategy?: 'union' | 'intersection' | 'semantic-first' | 'structured-first';
|
|
/** Weights for score combination */
|
|
weights?: {
|
|
semantic: number;
|
|
structured: number;
|
|
};
|
|
}
|
|
/**
|
|
* HybridBackend Implementation
|
|
*
|
|
* Intelligently routes queries between SQLite and AgentDB:
|
|
* - Exact matches, prefix queries → SQLite
|
|
* - Semantic search, similarity → AgentDB
|
|
* - Complex hybrid queries → Both backends with intelligent merging
|
|
*/
|
|
export declare class HybridBackend extends EventEmitter implements IMemoryBackend {
|
|
private sqlite;
|
|
private agentdb;
|
|
private config;
|
|
private initialized;
|
|
private stats;
|
|
constructor(config?: HybridBackendConfig);
|
|
/**
|
|
* Initialize both backends
|
|
*/
|
|
initialize(): Promise<void>;
|
|
/**
|
|
* Shutdown both backends
|
|
*/
|
|
shutdown(): Promise<void>;
|
|
/**
|
|
* Store in both backends (dual-write for consistency)
|
|
*/
|
|
store(entry: MemoryEntry): Promise<void>;
|
|
/**
|
|
* Get from AgentDB (has caching enabled)
|
|
*/
|
|
get(id: string): Promise<MemoryEntry | null>;
|
|
/**
|
|
* Get by key (SQLite optimized for exact matches)
|
|
*/
|
|
getByKey(namespace: string, key: string): Promise<MemoryEntry | null>;
|
|
/**
|
|
* Update in both backends
|
|
*/
|
|
update(id: string, update: MemoryEntryUpdate): Promise<MemoryEntry | null>;
|
|
/**
|
|
* Delete from both backends
|
|
*/
|
|
delete(id: string): Promise<boolean>;
|
|
/**
|
|
* Query routing - semantic goes to AgentDB, structured to SQLite
|
|
*/
|
|
query(query: MemoryQuery): Promise<MemoryEntry[]>;
|
|
/**
|
|
* Structured queries (SQL)
|
|
* Routes to SQLite for optimal performance
|
|
*/
|
|
queryStructured(query: StructuredQuery): Promise<MemoryEntry[]>;
|
|
/**
|
|
* Semantic queries (vector)
|
|
* Routes to AgentDB for HNSW-based vector search
|
|
*/
|
|
querySemantic(query: SemanticQuery): Promise<MemoryEntry[]>;
|
|
/**
|
|
* Hybrid queries (combine both)
|
|
* Intelligently merges results from both backends
|
|
*/
|
|
queryHybrid(query: HybridQuery): Promise<MemoryEntry[]>;
|
|
/**
|
|
* Semantic vector search (routes to AgentDB)
|
|
*/
|
|
search(embedding: Float32Array, options: SearchOptions): Promise<SearchResult[]>;
|
|
/**
|
|
* Bulk insert to both backends
|
|
*/
|
|
bulkInsert(entries: MemoryEntry[]): Promise<void>;
|
|
/**
|
|
* Bulk delete from both backends
|
|
*/
|
|
bulkDelete(ids: string[]): Promise<number>;
|
|
/**
|
|
* Count entries (use SQLite for efficiency)
|
|
*/
|
|
count(namespace?: string): Promise<number>;
|
|
/**
|
|
* List namespaces (use SQLite)
|
|
*/
|
|
listNamespaces(): Promise<string[]>;
|
|
/**
|
|
* Clear namespace in both backends
|
|
*/
|
|
clearNamespace(namespace: string): Promise<number>;
|
|
/**
|
|
* Get combined statistics from both backends
|
|
*/
|
|
getStats(): Promise<BackendStats>;
|
|
/**
|
|
* Health check for both backends
|
|
*/
|
|
healthCheck(): Promise<HealthCheckResult>;
|
|
/**
|
|
* Auto-route queries based on properties
|
|
*/
|
|
private autoRoute;
|
|
/**
|
|
* Internal hybrid query implementation
|
|
*/
|
|
private queryHybridInternal;
|
|
/**
|
|
* Combine results using union (all unique results)
|
|
*/
|
|
private combineUnion;
|
|
/**
|
|
* Combine results using intersection (only common results)
|
|
*/
|
|
private combineIntersection;
|
|
/**
|
|
* Semantic-first: Prefer semantic results, add structured if not present
|
|
*/
|
|
private combineSemanticFirst;
|
|
/**
|
|
* Structured-first: Prefer structured results, add semantic if not present
|
|
*/
|
|
private combineStructuredFirst;
|
|
/**
|
|
* Record feedback for a memory entry.
|
|
* Delegates to AgentDB's recordFeedback when available.
|
|
* Gracefully degrades to a no-op when AgentDB is unavailable.
|
|
*/
|
|
recordFeedback(entryId: string, feedback: {
|
|
score: number;
|
|
label?: string;
|
|
context?: Record<string, unknown>;
|
|
}): Promise<boolean>;
|
|
/**
|
|
* Verify a witness chain for a memory entry.
|
|
* Delegates to AgentDB's verifyWitnessChain when available.
|
|
*/
|
|
verifyWitnessChain(entryId: string): Promise<{
|
|
valid: boolean;
|
|
chainLength: number;
|
|
errors: string[];
|
|
}>;
|
|
/**
|
|
* Get the witness chain for a memory entry.
|
|
* Delegates to AgentDB's getWitnessChain when available.
|
|
*/
|
|
getWitnessChain(entryId: string): Promise<Array<{
|
|
hash: string;
|
|
timestamp: number;
|
|
operation: string;
|
|
}>>;
|
|
/**
|
|
* Get underlying backends for advanced operations
|
|
*/
|
|
getSQLiteBackend(): SQLiteBackend;
|
|
getAgentDBBackend(): AgentDBBackend;
|
|
}
|
|
export default HybridBackend;
|
|
//# sourceMappingURL=hybrid-backend.d.ts.map
|