Initial commit

This commit is contained in:
David Snelling 2025-06-24 11:41:30 -07:00
commit 5a8a6c1ba3
81 changed files with 39269 additions and 0 deletions

251
src/augmentations/README.md Normal file
View file

@ -0,0 +1,251 @@
<div align="center">
<img src="../../brainy.png" alt="Brainy Logo" width="200"/>
# Brainy Augmentations
</div>
This directory contains the augmentation implementations for Brainy. Augmentations are pluggable components that extend
Brainy's functionality in various ways.
## Available Augmentations
### Conduit Augmentations
Conduit augmentations provide data synchronization between Brainy instances.
#### WebSocketConduitAugmentation
A conduit augmentation that syncs Brainy instances using WebSockets. This is used for syncing between browsers and
servers, or between servers.
```javascript
import { createConduitAugmentation, augmentationPipeline } from '@soulcraft/brainy'
// Create a WebSocket conduit augmentation
const wsConduit = await createConduitAugmentation('websocket', 'my-websocket-sync')
// Register the augmentation with the pipeline
augmentationPipeline.register(wsConduit)
// Connect to another Brainy instance
const connectionResult = await wsConduit.establishConnection(
'wss://your-websocket-server.com/brainy-sync',
{ protocols: 'brainy-sync' }
)
```
#### WebRTCConduitAugmentation
A conduit augmentation that syncs Brainy instances using WebRTC. This is used for direct peer-to-peer syncing between
browsers.
```javascript
import { createConduitAugmentation, augmentationPipeline } from '@soulcraft/brainy'
// Create a WebRTC conduit augmentation
const webrtcConduit = await createConduitAugmentation('webrtc', 'my-webrtc-sync')
// Register the augmentation with the pipeline
augmentationPipeline.register(webrtcConduit)
// Connect to a peer
const connectionResult = await webrtcConduit.establishConnection(
'peer-id-to-connect-to',
{
signalServerUrl: 'wss://your-signal-server.com',
localPeerId: 'my-peer-id',
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
}
)
```
#### ServerSearchConduitAugmentation
A specialized conduit augmentation that provides functionality for searching a server-hosted Brainy instance and storing
results locally. This allows you to:
- Search a server-hosted Brainy instance from a browser
- Store the search results in a local Brainy instance
- Perform further searches against the local instance without needing to query the server again
- Add data to both local and server instances
```javascript
import {
ServerSearchConduitAugmentation,
createServerSearchAugmentations,
augmentationPipeline
} from '@soulcraft/brainy'
// Using the factory function (recommended)
const { conduit, activation, connection } = await createServerSearchAugmentations(
'wss://your-brainy-server.com/ws',
{ protocols: 'brainy-sync' }
)
// Register the augmentations with the pipeline
augmentationPipeline.register(conduit)
augmentationPipeline.register(activation)
// Search the server and store results locally
const serverSearchResult = await conduit.searchServer(
connection.connectionId,
'your search query',
5 // limit
)
// Search the local instance
const localSearchResult = await conduit.searchLocal('your search query', 5)
// Perform a combined search (local first, then server if needed)
const combinedSearchResult = await conduit.searchCombined(
connection.connectionId,
'your search query',
5
)
// Add data to both local and server
const addResult = await conduit.addToBoth(
connection.connectionId,
'Text to add',
{ /* metadata */ }
)
```
### Activation Augmentations
Activation augmentations dictate how Brainy initiates actions, responses, or data manipulations.
#### ServerSearchActivationAugmentation
An activation augmentation that provides actions for server search functionality. This works in conjunction with the
ServerSearchConduitAugmentation to provide a complete solution for browser-server search.
```javascript
import {
ServerSearchActivationAugmentation,
createServerSearchAugmentations,
augmentationPipeline
} from '@soulcraft/brainy'
// Using the factory function (recommended)
const { conduit, activation, connection } = await createServerSearchAugmentations(
'wss://your-brainy-server.com/ws',
{ protocols: 'brainy-sync' }
)
// Register the augmentations with the pipeline
augmentationPipeline.register(conduit)
augmentationPipeline.register(activation)
// Use the activation augmentation to search the server
const serverSearchAction = activation.triggerAction('searchServer', {
connectionId: connection.connectionId,
query: 'your search query',
limit: 5
})
if (serverSearchAction.success) {
// The data property contains a promise that will resolve to the search results
const serverSearchResult = await serverSearchAction.data
console.log('Server search results:', serverSearchResult)
}
// Other available actions:
// - 'connectToServer': Connect to a server
// - 'searchLocal': Search the local instance
// - 'searchCombined': Search both local and server
// - 'addToBoth': Add data to both local and server
```
## Using the Augmentation Pipeline
The augmentation pipeline provides a way to execute augmentations based on their type.
```javascript
import { augmentationPipeline } from '@soulcraft/brainy'
// Execute a conduit augmentation
const conduitResults = await augmentationPipeline.executeConduitPipeline(
'methodName',
[arg1, arg2, ...],
{ /* options */ }
)
// Execute an activation augmentation
const activationResults = await augmentationPipeline.executeActivationPipeline(
'methodName',
[arg1, arg2, ...],
{ /* options */ }
)
```
## Creating Custom Augmentations
To create a custom augmentation, implement one of the augmentation interfaces:
- `ISenseAugmentation`: For processing raw data
- `IConduitAugmentation`: For data synchronization
- `ICognitionAugmentation`: For reasoning and inference
- `IMemoryAugmentation`: For data storage
- `IPerceptionAugmentation`: For data interpretation and visualization
- `IDialogAugmentation`: For natural language processing
- `IActivationAugmentation`: For triggering actions
Example:
```javascript
import { AugmentationType, IActivationAugmentation } from '@soulcraft/brainy'
class MyCustomActivation implements IActivationAugmentation {
readonly
name = 'my-custom-activation'
readonly
description = 'My custom activation augmentation'
enabled = true
getType(): AugmentationType {
return AugmentationType.ACTIVATION
}
async initialize(): Promise<void> {
// Initialization code
}
async shutDown(): Promise<void> {
// Cleanup code
}
async getStatus(): Promise<'active' | 'inactive' | 'error'> {
return 'active'
}
triggerAction(actionName: string, parameters
?:
Record<string, unknown>
):
AugmentationResponse<unknown> {
// Implementation
}
generateOutput(knowledgeId: string, format: string): AugmentationResponse<string | Record<string, unknown
>> {
// Implementation
}
interactExternal(systemId
:
string, payload
:
Record < string, unknown >
):
AugmentationResponse < unknown > {
// Implementation
}
}
```

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,333 @@
import {
AugmentationType,
IMemoryAugmentation,
AugmentationResponse
} from '../types/augmentations.js'
import { StorageAdapter, Vector } from '../coreTypes.js'
import { MemoryStorage } from '../storage/opfsStorage.js'
import { FileSystemStorage } from '../storage/fileSystemStorage.js'
import { OPFSStorage } from '../storage/opfsStorage.js'
import { cosineDistance } from '../utils/distance.js'
/**
* Base class for memory augmentations that wrap a StorageAdapter
*/
abstract class BaseMemoryAugmentation implements IMemoryAugmentation {
readonly name: string
readonly description: string = 'Base memory augmentation'
enabled: boolean = true
protected storage: StorageAdapter
protected isInitialized = false
constructor(name: string, storage: StorageAdapter) {
this.name = name
this.storage = storage
}
async initialize(): Promise<void> {
if (this.isInitialized) {
return
}
try {
await this.storage.init()
this.isInitialized = true
} catch (error) {
console.error(`Failed to initialize ${this.name}:`, error)
throw new Error(`Failed to initialize ${this.name}: ${error}`)
}
}
async shutDown(): Promise<void> {
this.isInitialized = false
}
async getStatus(): Promise<'active' | 'inactive' | 'error'> {
return this.isInitialized ? 'active' : 'inactive'
}
async storeData(
key: string,
data: unknown,
options?: Record<string, unknown>
): Promise<AugmentationResponse<boolean>> {
await this.ensureInitialized()
try {
await this.storage.saveMetadata(key, data)
return { success: true, data: true }
} catch (error) {
console.error(`Failed to store data for key ${key}:`, error)
return {
success: false,
data: false,
error: `Failed to store data: ${error}`
}
}
}
async retrieveData(
key: string,
options?: Record<string, unknown>
): Promise<AugmentationResponse<unknown>> {
await this.ensureInitialized()
try {
const data = await this.storage.getMetadata(key)
return {
success: true,
data
}
} catch (error) {
console.error(`Failed to retrieve data for key ${key}:`, error)
return {
success: false,
data: null,
error: `Failed to retrieve data: ${error}`
}
}
}
async updateData(
key: string,
data: unknown,
options?: Record<string, unknown>
): Promise<AugmentationResponse<boolean>> {
await this.ensureInitialized()
try {
await this.storage.saveMetadata(key, data)
return { success: true, data: true }
} catch (error) {
console.error(`Failed to update data for key ${key}:`, error)
return {
success: false,
data: false,
error: `Failed to update data: ${error}`
}
}
}
async deleteData(
key: string,
options?: Record<string, unknown>
): Promise<AugmentationResponse<boolean>> {
await this.ensureInitialized()
try {
// There's no direct deleteMetadata method, so we save null
await this.storage.saveMetadata(key, null)
return { success: true, data: true }
} catch (error) {
console.error(`Failed to delete data for key ${key}:`, error)
return {
success: false,
data: false,
error: `Failed to delete data: ${error}`
}
}
}
async listDataKeys(
pattern?: string,
options?: Record<string, unknown>
): Promise<AugmentationResponse<string[]>> {
// This is a limitation of the current StorageAdapter interface
// It doesn't provide a way to list all metadata keys
// We could implement this in the future by extending the StorageAdapter interface
return {
success: false,
data: [],
error: 'listDataKeys is not supported by this storage adapter'
}
}
/**
* Searches for data in the storage using vector similarity.
* Implements the findNearest functionality by calculating distances client-side.
* @param query The query vector or data to search for
* @param k Number of results to return (default: 10)
* @param options Optional search options
*/
async search(
query: unknown,
k: number = 10,
options?: Record<string, unknown>
): Promise<AugmentationResponse<Array<{
id: string;
score: number;
data: unknown;
}>>> {
await this.ensureInitialized()
try {
// Check if query is a vector
let queryVector: Vector
if (Array.isArray(query) && query.every(item => typeof item === 'number')) {
queryVector = query as Vector
} else {
// If query is not a vector, we can't perform vector search
return {
success: false,
data: [],
error: 'Query must be a vector (array of numbers) for vector search'
}
}
// Get all nodes from storage
const nodes = await this.storage.getAllNouns()
// Calculate distances and prepare results
const results: Array<{
id: string;
score: number;
data: unknown;
}> = []
for (const node of nodes) {
// Skip nodes that don't have a vector
if (!node.vector || !Array.isArray(node.vector)) {
continue
}
// Get metadata for the node
const metadata = await this.storage.getMetadata(node.id)
// Calculate distance between query vector and node vector
const distance = cosineDistance(queryVector, node.vector)
// Convert distance to similarity score (1 - distance for cosine)
// This way higher scores are better (more similar)
const score = 1 - distance
results.push({
id: node.id,
score,
data: metadata
})
}
// Sort results by score (descending) and take top k
results.sort((a, b) => b.score - a.score)
const topResults = results.slice(0, k)
return {
success: true,
data: topResults
}
} catch (error) {
console.error(`Failed to search in storage:`, error)
return {
success: false,
data: [],
error: `Failed to search in storage: ${error}`
}
}
}
protected async ensureInitialized(): Promise<void> {
if (!this.isInitialized) {
await this.initialize()
}
}
}
/**
* Memory augmentation that uses in-memory storage
*/
export class MemoryStorageAugmentation extends BaseMemoryAugmentation {
readonly description = 'Memory augmentation that stores data in memory'
enabled = true
constructor(name: string) {
super(name, new MemoryStorage())
}
getType(): AugmentationType {
return AugmentationType.MEMORY
}
}
/**
* Memory augmentation that uses file system storage
*/
export class FileSystemStorageAugmentation extends BaseMemoryAugmentation {
readonly description = 'Memory augmentation that stores data in the file system'
enabled = true
constructor(name: string, rootDirectory?: string) {
super(name, new FileSystemStorage(rootDirectory))
}
getType(): AugmentationType {
return AugmentationType.MEMORY
}
}
/**
* Memory augmentation that uses OPFS (Origin Private File System) storage
*/
export class OPFSStorageAugmentation extends BaseMemoryAugmentation {
readonly description = 'Memory augmentation that stores data in the Origin Private File System'
enabled = true
constructor(name: string) {
super(name, new OPFSStorage())
}
getType(): AugmentationType {
return AugmentationType.MEMORY
}
}
/**
* Factory function to create the appropriate memory augmentation based on the environment
*/
export async function createMemoryAugmentation(
name: string,
options: {
storageType?: 'memory' | 'filesystem' | 'opfs'
rootDirectory?: string
requestPersistentStorage?: boolean
} = {}
): Promise<IMemoryAugmentation> {
// If a specific storage type is requested, use that
if (options.storageType) {
switch (options.storageType) {
case 'memory':
return new MemoryStorageAugmentation(name)
case 'filesystem':
return new FileSystemStorageAugmentation(name, options.rootDirectory)
case 'opfs':
return new OPFSStorageAugmentation(name)
}
}
// Otherwise, select based on environment
// Use the global isNode variable from the environment detection
const isNodeEnv = globalThis.__ENV__?.isNode || (
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null
)
if (isNodeEnv) {
// In Node.js, use FileSystemStorage
return new FileSystemStorageAugmentation(name, options.rootDirectory)
} else {
// In browser, try OPFS first
const opfsStorage = new OPFSStorage()
if (opfsStorage.isOPFSAvailable()) {
// Request persistent storage if specified
if (options.requestPersistentStorage) {
await opfsStorage.requestPersistentStorage()
}
return new OPFSStorageAugmentation(name)
} else {
// Fall back to memory storage
return new MemoryStorageAugmentation(name)
}
}
}

View file

@ -0,0 +1,665 @@
/**
* 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,
IConduitAugmentation,
IActivationAugmentation,
IWebSocketSupport,
AugmentationResponse,
WebSocketConnection
} from '../types/augmentations.js'
import { WebSocketConduitAugmentation } from './conduitAugmentations.js'
import { v4 as uuidv4 } from 'uuid'
import { BrainyDataInterface } from '../types/brainyDataInterface.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 {
private localDb: BrainyDataInterface | null = null
constructor(name: string = 'server-search-conduit') {
super(name)
// this.description = 'Conduit augmentation for server-hosted Brainy search'
}
/**
* Initialize the augmentation
*/
async initialize(): Promise<void> {
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: BrainyDataInterface): void {
this.localDb = db
}
/**
* Get the local Brainy instance
* @returns The local Brainy instance
*/
getLocalDb(): BrainyDataInterface | null {
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: string,
query: string,
limit: number = 10
): Promise<AugmentationResponse<unknown>> {
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 as any[]
// 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: string,
limit: number = 10
): Promise<AugmentationResponse<unknown>> {
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: string,
query: string,
limit: number = 10
): Promise<AugmentationResponse<unknown>> {
await this.ensureInitialized()
try {
// Search local first
const localSearchResult = await this.searchLocal(query, limit)
if (!localSearchResult.success) {
return localSearchResult
}
const localResults = localSearchResult.data as any[]
// 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 as any[]
// 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: string,
data: string | any[],
metadata: any = {}
): Promise<AugmentationResponse<string>> {
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) as import('../coreTypes.js').VectorDocument<unknown>
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 implements IActivationAugmentation {
readonly name: string
readonly description: string
enabled: boolean = true
private isInitialized = false
private conduitAugmentation: ServerSearchConduitAugmentation | null = null
private connections: Map<string, WebSocketConnection> = new Map()
constructor(name: string = 'server-search-activation') {
this.name = name
this.description = 'Activation augmentation for server-hosted Brainy search'
}
getType(): AugmentationType {
return AugmentationType.ACTIVATION
}
/**
* Initialize the augmentation
*/
async initialize(): Promise<void> {
if (this.isInitialized) {
return
}
this.isInitialized = true
}
/**
* Shut down the augmentation
*/
async shutDown(): Promise<void> {
this.isInitialized = false
}
/**
* Get the status of the augmentation
*/
async getStatus(): Promise<'active' | 'inactive' | 'error'> {
return this.isInitialized ? 'active' : 'inactive'
}
/**
* Set the conduit augmentation to use for server search
* @param conduit The ServerSearchConduitAugmentation to use
*/
setConduitAugmentation(conduit: ServerSearchConduitAugmentation): void {
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: string, connection: WebSocketConnection): void {
this.connections.set(connectionId, connection)
}
/**
* Get a stored connection
* @param connectionId The ID of the connection to retrieve
* @returns The WebSocket connection
*/
getConnection(connectionId: string): WebSocketConnection | undefined {
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: string,
parameters?: Record<string, unknown>
): AugmentationResponse<unknown> {
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
*/
private handleConnectToServer(
parameters: Record<string, unknown>
): AugmentationResponse<unknown> {
const serverUrl = parameters.serverUrl as string
const protocols = parameters.protocols as string | string[] | undefined
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
*/
private handleSearchServer(
parameters: Record<string, unknown>
): AugmentationResponse<unknown> {
const connectionId = parameters.connectionId as string
const query = parameters.query as string
const limit = parameters.limit as number || 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
*/
private handleSearchLocal(
parameters: Record<string, unknown>
): AugmentationResponse<unknown> {
const query = parameters.query as string
const limit = parameters.limit as number || 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
*/
private handleSearchCombined(
parameters: Record<string, unknown>
): AugmentationResponse<unknown> {
const connectionId = parameters.connectionId as string
const query = parameters.query as string
const limit = parameters.limit as number || 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
*/
private handleAddToBoth(
parameters: Record<string, unknown>
): AugmentationResponse<unknown> {
const connectionId = parameters.connectionId as string
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 as any, metadata as any)
}
}
/**
* 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: string,
format: string
): AugmentationResponse<string | Record<string, unknown>> {
// 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: string,
payload: Record<string, unknown>
): AugmentationResponse<unknown> {
// 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: string,
options: {
conduitName?: string,
activationName?: string,
protocols?: string | string[],
localDb?: BrainyDataInterface
} = {}
): Promise<{
conduit: ServerSearchConduitAugmentation,
activation: ServerSearchActivationAugmentation,
connection: WebSocketConnection
}> {
// 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
}
}