UNPKG

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

577 lines (501 loc) 18.5 kB
/** * RuVector PostgreSQL Bridge - Semantic Code Search Example * * This example demonstrates: * - Embedding code snippets for semantic search * - Natural language code queries * - Ranking and relevance scoring * - Hybrid search (semantic + keyword) * * Run with: npx ts-node examples/ruvector/semantic-search.ts * * @module @claude-flow/plugins/examples/ruvector/semantic-search */ import { createRuVectorBridge, type RuVectorBridge, type VectorRecord, type VectorSearchOptions, } from '../../src/integrations/ruvector/index.js'; // ============================================================================ // Configuration // ============================================================================ const config = { connection: { host: process.env.POSTGRES_HOST || 'localhost', port: parseInt(process.env.POSTGRES_PORT || '5432', 10), database: process.env.POSTGRES_DB || 'vectors', user: process.env.POSTGRES_USER || 'postgres', password: process.env.POSTGRES_PASSWORD || 'postgres', }, dimensions: 768, // Code embedding dimension (e.g., CodeBERT, StarCoder) }; // ============================================================================ // Simulated Embedding Model // ============================================================================ /** * Simulated code embedding function. * In production, use a proper code embedding model like: * - CodeBERT * - StarCoder * - Code-LLaMA embeddings * - OpenAI text-embedding-3-small */ class CodeEmbedder { private dimension: number; private vocabulary: Map<string, number[]> = new Map(); constructor(dimension: number) { this.dimension = dimension; this.initializeVocabulary(); } /** * Initialize semantic vocabulary for code concepts. * This is a simplified simulation - real models use neural networks. */ private initializeVocabulary(): void { const concepts = [ 'function', 'class', 'variable', 'loop', 'condition', 'array', 'object', 'string', 'number', 'boolean', 'async', 'await', 'promise', 'callback', 'error', 'import', 'export', 'module', 'interface', 'type', 'auth', 'user', 'password', 'token', 'login', 'database', 'query', 'sql', 'insert', 'select', 'api', 'http', 'request', 'response', 'endpoint', 'test', 'expect', 'mock', 'assert', 'describe', ]; concepts.forEach((concept, idx) => { const embedding = this.createConceptEmbedding(idx, concepts.length); this.vocabulary.set(concept, embedding); }); } /** * Create a deterministic embedding for a concept. */ private createConceptEmbedding(idx: number, total: number): number[] { const embedding = new Array(this.dimension).fill(0); const base = (idx / total) * Math.PI * 2; for (let i = 0; i < this.dimension; i++) { embedding[i] = Math.sin(base + (i / this.dimension) * Math.PI) * 0.5; if (i % 2 === 0) { embedding[i] += Math.cos(base * 2 + i * 0.01) * 0.3; } } return this.normalize(embedding); } /** * Embed code snippet. */ embed(code: string): number[] { const tokens = this.tokenize(code); const embedding = new Array(this.dimension).fill(0); // Accumulate embeddings from vocabulary matches let matchCount = 0; for (const token of tokens) { const tokenLower = token.toLowerCase(); if (this.vocabulary.has(tokenLower)) { const tokenEmbedding = this.vocabulary.get(tokenLower)!; for (let i = 0; i < this.dimension; i++) { embedding[i] += tokenEmbedding[i]; } matchCount++; } } // Add noise for tokens not in vocabulary const unknownRatio = (tokens.length - matchCount) / tokens.length; for (let i = 0; i < this.dimension; i++) { embedding[i] += (Math.random() * 2 - 1) * unknownRatio * 0.1; } return this.normalize(embedding); } /** * Embed a natural language query. */ embedQuery(query: string): number[] { return this.embed(query); } /** * Tokenize code into meaningful tokens. */ private tokenize(code: string): string[] { return code .replace(/[^\w\s]/g, ' ') .split(/\s+/) .filter(t => t.length > 1); } /** * Normalize vector to unit length. */ private normalize(vec: number[]): number[] { const magnitude = Math.sqrt(vec.reduce((sum, v) => sum + v * v, 0)); if (magnitude === 0) return vec; return vec.map(v => v / magnitude); } } // ============================================================================ // Sample Code Repository // ============================================================================ const codeSnippets = [ { id: 'auth-login', language: 'typescript', filename: 'auth/login.ts', code: ` async function login(username: string, password: string): Promise<AuthToken> { const user = await findUserByUsername(username); if (!user) { throw new AuthenticationError('User not found'); } const isValid = await verifyPassword(password, user.passwordHash); if (!isValid) { throw new AuthenticationError('Invalid password'); } const token = generateJWT({ userId: user.id, role: user.role }); await saveSession(user.id, token); return { token, expiresIn: 3600 }; }`, tags: ['authentication', 'login', 'jwt', 'security'], }, { id: 'auth-middleware', language: 'typescript', filename: 'middleware/auth.ts', code: ` export function authMiddleware(req: Request, res: Response, next: NextFunction) { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) { return res.status(401).json({ error: 'No token provided' }); } try { const decoded = verifyJWT(token); req.user = decoded; next(); } catch (error) { return res.status(403).json({ error: 'Invalid token' }); } }`, tags: ['middleware', 'authentication', 'jwt', 'express'], }, { id: 'db-query', language: 'typescript', filename: 'database/query.ts', code: ` async function executeQuery<T>(sql: string, params: unknown[]): Promise<T[]> { const client = await pool.connect(); try { const result = await client.query(sql, params); return result.rows; } catch (error) { logger.error('Database query failed', { sql, error }); throw new DatabaseError('Query execution failed'); } finally { client.release(); } }`, tags: ['database', 'postgresql', 'query', 'async'], }, { id: 'api-users', language: 'typescript', filename: 'api/users.ts', code: ` router.get('/users/:id', async (req: Request, res: Response) => { const { id } = req.params; const user = await userService.findById(id); if (!user) { return res.status(404).json({ error: 'User not found' }); } res.json({ id: user.id, username: user.username, email: user.email, createdAt: user.createdAt, }); });`, tags: ['api', 'rest', 'users', 'express'], }, { id: 'test-auth', language: 'typescript', filename: 'tests/auth.test.ts', code: ` describe('Authentication', () => { it('should login with valid credentials', async () => { const result = await login('testuser', 'password123'); expect(result.token).toBeDefined(); expect(result.expiresIn).toBe(3600); }); it('should reject invalid password', async () => { await expect(login('testuser', 'wrong')) .rejects .toThrow('Invalid password'); }); });`, tags: ['test', 'jest', 'authentication', 'unit-test'], }, { id: 'array-utils', language: 'typescript', filename: 'utils/array.ts', code: ` export function chunk<T>(array: T[], size: number): T[][] { const chunks: T[][] = []; for (let i = 0; i < array.length; i += size) { chunks.push(array.slice(i, i + size)); } return chunks; } export function unique<T>(array: T[]): T[] { return [...new Set(array)]; } export function groupBy<T>(array: T[], key: keyof T): Record<string, T[]> { return array.reduce((acc, item) => { const groupKey = String(item[key]); acc[groupKey] = acc[groupKey] || []; acc[groupKey].push(item); return acc; }, {} as Record<string, T[]>); }`, tags: ['utility', 'array', 'functional'], }, { id: 'error-handler', language: 'typescript', filename: 'middleware/error.ts', code: ` export function errorHandler( error: Error, req: Request, res: Response, next: NextFunction ) { logger.error('Unhandled error', { error, path: req.path }); if (error instanceof ValidationError) { return res.status(400).json({ error: error.message }); } if (error instanceof AuthenticationError) { return res.status(401).json({ error: error.message }); } res.status(500).json({ error: 'Internal server error' }); }`, tags: ['error-handling', 'middleware', 'express'], }, { id: 'cache-service', language: 'typescript', filename: 'services/cache.ts', code: ` class CacheService { private cache: Map<string, { value: unknown; expiry: number }> = new Map(); async get<T>(key: string): Promise<T | null> { const entry = this.cache.get(key); if (!entry) return null; if (Date.now() > entry.expiry) { this.cache.delete(key); return null; } return entry.value as T; } async set(key: string, value: unknown, ttlSeconds: number): Promise<void> { this.cache.set(key, { value, expiry: Date.now() + ttlSeconds * 1000, }); } }`, tags: ['cache', 'memory', 'service'], }, ]; // ============================================================================ // Main Example // ============================================================================ async function main(): Promise<void> { console.log('RuVector PostgreSQL Bridge - Semantic Code Search Example'); console.log('=========================================================\n'); const bridge: RuVectorBridge = createRuVectorBridge({ connectionString: `postgresql://${config.connection.user}:${config.connection.password}@${config.connection.host}:${config.connection.port}/${config.connection.database}`, }); const embedder = new CodeEmbedder(config.dimensions); try { await bridge.connect(); console.log('Connected to PostgreSQL\n'); // ======================================================================== // 1. Create Code Collection // ======================================================================== console.log('1. Creating code collection...'); await bridge.createCollection('code_snippets', { dimensions: config.dimensions, distanceMetric: 'cosine', indexType: 'hnsw', indexParams: { m: 16, efConstruction: 100 }, }); console.log(' Collection created!\n'); // ======================================================================== // 2. Index Code Snippets // ======================================================================== console.log('2. Indexing code snippets...'); for (const snippet of codeSnippets) { const embedding = embedder.embed(snippet.code); await bridge.insert('code_snippets', { id: snippet.id, embedding, metadata: { language: snippet.language, filename: snippet.filename, tags: snippet.tags, codePreview: snippet.code.slice(0, 100) + '...', }, }); console.log(` Indexed: ${snippet.filename}`); } console.log(' All snippets indexed!\n'); // ======================================================================== // 3. Natural Language Code Search // ======================================================================== console.log('3. Natural language search examples:\n'); const queries = [ 'How do I authenticate a user with username and password?', 'Show me how to handle errors in Express middleware', 'I need to query a PostgreSQL database', 'Unit test example for login functionality', 'How to implement caching with expiration?', ]; for (const query of queries) { console.log(` Query: "${query}"`); const queryEmbedding = embedder.embedQuery(query); const results = await bridge.search('code_snippets', queryEmbedding, { k: 2, includeMetadata: true, includeDistance: true, }); console.log(' Results:'); results.forEach((result, i) => { const similarity = 1 - (result.distance ?? 0); console.log(` ${i + 1}. ${result.metadata?.filename} (similarity: ${(similarity * 100).toFixed(1)}%)`); console.log(` Tags: ${(result.metadata?.tags as string[])?.join(', ')}`); }); console.log(); } // ======================================================================== // 4. Tag-Filtered Search // ======================================================================== console.log('4. Tag-filtered search (authentication + middleware)...\n'); const authQuery = 'verify user credentials and protect routes'; const authEmbedding = embedder.embedQuery(authQuery); const authResults = await bridge.search('code_snippets', authEmbedding, { k: 5, includeMetadata: true, includeDistance: true, filter: { tags: { $contains: 'authentication' }, }, }); console.log(` Query: "${authQuery}"`); console.log(' Filtered to: authentication tag'); console.log(' Results:'); authResults.forEach((result, i) => { const similarity = 1 - (result.distance ?? 0); console.log(` ${i + 1}. ${result.metadata?.filename} (similarity: ${(similarity * 100).toFixed(1)}%)`); }); console.log(); // ======================================================================== // 5. Hybrid Search (Semantic + Keyword) // ======================================================================== console.log('5. Hybrid search (semantic + keyword matching)...\n'); const hybridQuery = 'async function database'; const hybridEmbedding = embedder.embedQuery(hybridQuery); // First, get semantic results const semanticResults = await bridge.search('code_snippets', hybridEmbedding, { k: 10, includeMetadata: true, includeDistance: true, }); // Then, boost results that contain the keyword const keyword = 'async'; const hybridResults = semanticResults .map(result => { const code = codeSnippets.find(s => s.id === result.id)?.code ?? ''; const keywordMatches = (code.match(new RegExp(keyword, 'gi')) || []).length; const semanticScore = 1 - (result.distance ?? 0); const keywordBoost = Math.min(keywordMatches * 0.05, 0.2); const hybridScore = semanticScore * 0.7 + keywordBoost + 0.1; return { ...result, hybridScore }; }) .sort((a, b) => b.hybridScore - a.hybridScore) .slice(0, 3); console.log(` Query: "${hybridQuery}"`); console.log(' Results (semantic 70% + keyword boost 30%):'); hybridResults.forEach((result, i) => { console.log(` ${i + 1}. ${result.metadata?.filename} (hybrid score: ${(result.hybridScore * 100).toFixed(1)}%)`); }); console.log(); // ======================================================================== // 6. Similar Code Detection // ======================================================================== console.log('6. Finding similar code to a reference snippet...\n'); const referenceId = 'auth-login'; const reference = await bridge.get('code_snippets', referenceId); if (reference) { const similarResults = await bridge.search('code_snippets', reference.embedding, { k: 4, includeMetadata: true, includeDistance: true, }); console.log(` Reference: ${reference.metadata?.filename}`); console.log(' Similar code:'); similarResults .filter(r => r.id !== referenceId) // Exclude self .forEach((result, i) => { const similarity = 1 - (result.distance ?? 0); console.log(` ${i + 1}. ${result.metadata?.filename} (similarity: ${(similarity * 100).toFixed(1)}%)`); }); } console.log(); // ======================================================================== // 7. Relevance Feedback (Re-ranking) // ======================================================================== console.log('7. Relevance feedback / re-ranking demo...\n'); // User marks 'auth-middleware' as relevant for authentication queries const relevantId = 'auth-middleware'; const relevantDoc = await bridge.get('code_snippets', relevantId); if (relevantDoc) { // Combine original query with relevant document embedding const originalQuery = 'protect API endpoints'; const originalEmbedding = embedder.embedQuery(originalQuery); // Rocchio algorithm: query' = alpha * query + beta * relevant const alpha = 0.7; const beta = 0.3; const expandedEmbedding = originalEmbedding.map( (v, i) => alpha * v + beta * relevantDoc.embedding[i] ); // Normalize const magnitude = Math.sqrt(expandedEmbedding.reduce((s, v) => s + v * v, 0)); const normalizedEmbedding = expandedEmbedding.map(v => v / magnitude); const rerankedResults = await bridge.search('code_snippets', normalizedEmbedding, { k: 3, includeMetadata: true, includeDistance: true, }); console.log(` Original query: "${originalQuery}"`); console.log(` Relevant feedback: ${relevantDoc.metadata?.filename}`); console.log(' Re-ranked results:'); rerankedResults.forEach((result, i) => { const similarity = 1 - (result.distance ?? 0); console.log(` ${i + 1}. ${result.metadata?.filename} (similarity: ${(similarity * 100).toFixed(1)}%)`); }); } // ======================================================================== // Cleanup // ======================================================================== console.log('\n' + '='.repeat(60)); console.log('Semantic code search example completed!'); console.log('='.repeat(60)); } catch (error) { console.error('Error:', error); throw error; } finally { await bridge.disconnect(); console.log('\nDisconnected from PostgreSQL.'); } } main().catch(console.error);