/** * Server Search Augmentations * * This file implements conduit and activation augmentations for browser-server search functionality. * It allows Brainy to search a server-hosted instance and store results locally. */ import { AugmentationType } from '../types/augmentations.js'; import { WebSocketConduitAugmentation } from './conduitAugmentations.js'; /** * ServerSearchConduitAugmentation * * A specialized conduit augmentation that provides functionality for searching * a server-hosted Brainy instance and storing results locally. */ export class ServerSearchConduitAugmentation extends WebSocketConduitAugmentation { constructor(name = 'server-search-conduit') { super(name); this.localDb = null; // this.description = 'Conduit augmentation for server-hosted Brainy search' } /** * Initialize the augmentation */ async initialize() { if (this.isInitialized) { return; } try { // Initialize the base conduit await super.initialize(); // Local DB must be set before initialization if (!this.localDb) { throw new Error('Local database not set. Call setLocalDb before initializing.'); } this.isInitialized = true; } catch (error) { console.error(`Failed to initialize ${this.name}:`, error); throw new Error(`Failed to initialize ${this.name}: ${error}`); } } /** * Set the local Brainy instance * @param db The Brainy instance to use for local storage */ setLocalDb(db) { this.localDb = db; } /** * Get the local Brainy instance * @returns The local Brainy instance */ getLocalDb() { return this.localDb; } /** * Search the server-hosted Brainy instance and store results locally * @param connectionId The ID of the established connection * @param query The search query * @param limit Maximum number of results to return * @returns Search results */ async searchServer(connectionId, query, limit = 10) { await this.ensureInitialized(); try { // Create a search request const readResult = await this.readData({ connectionId, query: { type: 'search', query, limit } }); if (readResult.success && readResult.data) { const searchResults = readResult.data; // Store the results in the local Brainy instance if (this.localDb) { for (const result of searchResults) { // Check if the noun already exists in the local database const existingNoun = await this.localDb.get(result.id); if (!existingNoun) { // Add the noun to the local database await this.localDb.add(result.vector, result.metadata); } } } return { success: true, data: searchResults }; } else { return { success: false, data: null, error: readResult.error || 'Unknown error searching server' }; } } catch (error) { console.error('Error searching server:', error); return { success: false, data: null, error: `Error searching server: ${error}` }; } } /** * Search the local Brainy instance * @param query The search query * @param limit Maximum number of results to return * @returns Search results */ async searchLocal(query, limit = 10) { await this.ensureInitialized(); try { if (!this.localDb) { return { success: false, data: null, error: 'Local database not initialized' }; } const results = await this.localDb.searchText(query, limit); return { success: true, data: results }; } catch (error) { console.error('Error searching local database:', error); return { success: false, data: null, error: `Error searching local database: ${error}` }; } } /** * Search both server and local instances, combine results, and store server results locally * @param connectionId The ID of the established connection * @param query The search query * @param limit Maximum number of results to return * @returns Combined search results */ async searchCombined(connectionId, query, limit = 10) { await this.ensureInitialized(); try { // Search local first const localSearchResult = await this.searchLocal(query, limit); if (!localSearchResult.success) { return localSearchResult; } const localResults = localSearchResult.data; // If we have enough local results, return them if (localResults.length >= limit) { return localSearchResult; } // Otherwise, search server for additional results const serverSearchResult = await this.searchServer(connectionId, query, limit - localResults.length); if (!serverSearchResult.success) { // If server search fails, return local results return localSearchResult; } const serverResults = serverSearchResult.data; // Combine results, removing duplicates const combinedResults = [...localResults]; const localIds = new Set(localResults.map((r) => r.id)); for (const result of serverResults) { if (!localIds.has(result.id)) { combinedResults.push(result); } } return { success: true, data: combinedResults }; } catch (error) { console.error('Error performing combined search:', error); return { success: false, data: null, error: `Error performing combined search: ${error}` }; } } /** * Add data to both local and server instances * @param connectionId The ID of the established connection * @param data Text or vector to add * @param metadata Metadata for the data * @returns ID of the added data */ async addToBoth(connectionId, data, metadata = {}) { await this.ensureInitialized(); try { if (!this.localDb) { return { success: false, data: '', error: 'Local database not initialized' }; } // Add to local first const id = await this.localDb.add(data, metadata); // Get the vector and metadata const noun = (await this.localDb.get(id)); if (!noun) { return { success: false, data: '', error: 'Failed to retrieve newly created noun' }; } // Add to server const writeResult = await this.writeData({ connectionId, data: { type: 'addNoun', vector: noun.vector, metadata: noun.metadata } }); if (!writeResult.success) { return { success: true, data: id, error: `Added locally but failed to add to server: ${writeResult.error}` }; } return { success: true, data: id }; } catch (error) { console.error('Error adding data to both:', error); return { success: false, data: '', error: `Error adding data to both: ${error}` }; } } } /** * ServerSearchActivationAugmentation * * An activation augmentation that provides actions for server search functionality. */ export class ServerSearchActivationAugmentation { constructor(name = 'server-search-activation') { this.enabled = true; this.isInitialized = false; this.conduitAugmentation = null; this.connections = new Map(); this.name = name; this.description = 'Activation augmentation for server-hosted Brainy search'; } getType() { return AugmentationType.ACTIVATION; } /** * Initialize the augmentation */ async initialize() { if (this.isInitialized) { return; } this.isInitialized = true; } /** * Shut down the augmentation */ async shutDown() { this.isInitialized = false; } /** * Get the status of the augmentation */ async getStatus() { return this.isInitialized ? 'active' : 'inactive'; } /** * Set the conduit augmentation to use for server search * @param conduit The ServerSearchConduitAugmentation to use */ setConduitAugmentation(conduit) { this.conduitAugmentation = conduit; } /** * Store a connection for later use * @param connectionId The ID to use for the connection * @param connection The WebSocket connection */ storeConnection(connectionId, connection) { this.connections.set(connectionId, connection); } /** * Get a stored connection * @param connectionId The ID of the connection to retrieve * @returns The WebSocket connection */ getConnection(connectionId) { return this.connections.get(connectionId); } /** * Trigger an action based on a processed command or internal state * @param actionName The name of the action to trigger * @param parameters Optional parameters for the action */ triggerAction(actionName, parameters) { if (!this.conduitAugmentation) { return { success: false, data: null, error: 'Conduit augmentation not set' }; } // Handle different actions switch (actionName) { case 'connectToServer': return this.handleConnectToServer(parameters || {}); case 'searchServer': return this.handleSearchServer(parameters || {}); case 'searchLocal': return this.handleSearchLocal(parameters || {}); case 'searchCombined': return this.handleSearchCombined(parameters || {}); case 'addToBoth': return this.handleAddToBoth(parameters || {}); default: return { success: false, data: null, error: `Unknown action: ${actionName}` }; } } /** * Handle the connectToServer action * @param parameters Action parameters */ handleConnectToServer(parameters) { const serverUrl = parameters.serverUrl; const protocols = parameters.protocols; if (!serverUrl) { return { success: false, data: null, error: 'serverUrl parameter is required' }; } // Return a promise that will be resolved when the connection is established return { success: true, data: this.conduitAugmentation.establishConnection(serverUrl, { protocols }) }; } /** * Handle the searchServer action * @param parameters Action parameters */ handleSearchServer(parameters) { const connectionId = parameters.connectionId; const query = parameters.query; const limit = parameters.limit || 10; if (!connectionId) { return { success: false, data: null, error: 'connectionId parameter is required' }; } if (!query) { return { success: false, data: null, error: 'query parameter is required' }; } // Return a promise that will be resolved when the search is complete return { success: true, data: this.conduitAugmentation.searchServer(connectionId, query, limit) }; } /** * Handle the searchLocal action * @param parameters Action parameters */ handleSearchLocal(parameters) { const query = parameters.query; const limit = parameters.limit || 10; if (!query) { return { success: false, data: null, error: 'query parameter is required' }; } // Return a promise that will be resolved when the search is complete return { success: true, data: this.conduitAugmentation.searchLocal(query, limit) }; } /** * Handle the searchCombined action * @param parameters Action parameters */ handleSearchCombined(parameters) { const connectionId = parameters.connectionId; const query = parameters.query; const limit = parameters.limit || 10; if (!connectionId) { return { success: false, data: null, error: 'connectionId parameter is required' }; } if (!query) { return { success: false, data: null, error: 'query parameter is required' }; } // Return a promise that will be resolved when the search is complete return { success: true, data: this.conduitAugmentation.searchCombined(connectionId, query, limit) }; } /** * Handle the addToBoth action * @param parameters Action parameters */ handleAddToBoth(parameters) { const connectionId = parameters.connectionId; const data = parameters.data; const metadata = parameters.metadata || {}; if (!connectionId) { return { success: false, data: null, error: 'connectionId parameter is required' }; } if (!data) { return { success: false, data: null, error: 'data parameter is required' }; } // Return a promise that will be resolved when the add is complete return { success: true, data: this.conduitAugmentation.addToBoth(connectionId, data, metadata) }; } /** * Generates an expressive output or response from Brainy * @param knowledgeId The identifier of the knowledge to express * @param format The desired output format (e.g., 'text', 'json') */ generateOutput(knowledgeId, format) { // This method is not used for server search functionality return { success: false, data: '', error: 'generateOutput is not implemented for ServerSearchActivationAugmentation' }; } /** * Interacts with an external system or API * @param systemId The identifier of the external system * @param payload The data to send to the external system */ interactExternal(systemId, payload) { // This method is not used for server search functionality return { success: false, data: null, error: 'interactExternal is not implemented for ServerSearchActivationAugmentation' }; } } /** * Factory function to create server search augmentations * @param serverUrl The URL of the server to connect to * @param options Additional options * @returns An object containing the created augmentations */ export async function createServerSearchAugmentations(serverUrl, options = {}) { // Create the conduit augmentation const conduit = new ServerSearchConduitAugmentation(options.conduitName); await conduit.initialize(); // Set the local database if provided if (options.localDb) { conduit.setLocalDb(options.localDb); } // Create the activation augmentation const activation = new ServerSearchActivationAugmentation(options.activationName); await activation.initialize(); // Link the augmentations activation.setConduitAugmentation(conduit); // Connect to the server const connectionResult = await conduit.establishConnection(serverUrl, { protocols: options.protocols }); if (!connectionResult.success || !connectionResult.data) { throw new Error(`Failed to connect to server: ${connectionResult.error}`); } const connection = connectionResult.data; // Store the connection in the activation augmentation activation.storeConnection(connection.connectionId, connection); return { conduit, activation, connection }; } //# sourceMappingURL=serverSearchAugmentations.js.map