UNPKG

@hashgraphonline/conversational-agent

Version:

Hashgraph Online conversational AI agent implementing HCS-10 communication, HCS-2 registries, and content inscription on Hedera. https://hol.org

281 lines (252 loc) 7.76 kB
import { ContentStorage } from '../memory/content-storage'; import { ContentStoreService, extractReferenceId, shouldUseReference, ContentResolverRegistry, type ContentStoreInterface, type ContentResolverInterface, type ReferenceResolutionResult, } from '@hashgraphonline/standards-sdk'; import type { ContentReference, ContentReferenceConfig, ContentReferenceStats, } from '../types/content-reference'; import { Logger } from '@hashgraphonline/standards-sdk'; /** * Content metadata interface for adapter compatibility */ interface AdapterContentMetadata { mimeType?: string; fileName?: string; sizeBytes?: number; [key: string]: unknown; } /** * Configuration interface for content storage */ interface ContentStoreConfig { maxSize?: number; enableCompression?: boolean; [key: string]: unknown; } /** * Adapter to make ContentStorage compatible with ContentStoreInterface */ class ContentStorageAdapter implements ContentStoreInterface { constructor(private storage: ContentStorage) {} async storeContent( content: Buffer, metadata: AdapterContentMetadata ): Promise<string> { const storeMetadata = { contentType: 'binary' as const, sizeBytes: content.length, source: 'system' as const, ...metadata, }; const contentRef = await this.storage.storeContent(content, storeMetadata); return contentRef.referenceId; } async resolveReference( referenceId: string ): Promise<ReferenceResolutionResult> { const result = await this.storage.resolveReference(referenceId); if (result.success && result.content) { const response: ReferenceResolutionResult = { content: result.content, }; if (result.metadata) { response.metadata = { ...(result.metadata.mimeType !== undefined && { mimeType: result.metadata.mimeType, }), ...(result.metadata.fileName !== undefined && { fileName: result.metadata.fileName, }), originalSize: result.metadata.sizeBytes, }; } return response; } else { throw new Error(result.error || 'Reference not found'); } } async hasReference(referenceId: string): Promise<boolean> { return await this.storage.hasReference(referenceId); } async cleanupReference(referenceId: string): Promise<void> { await this.storage.cleanupReference(referenceId); } async getStats(): Promise<unknown> { return await this.storage.getStats(); } async updateConfig(config: ContentStoreConfig): Promise<void> { const referenceConfig = { sizeThresholdBytes: config.maxSize || 10240, enableAutoCleanup: config.enableCompression || true, ...config, }; return await this.storage.updateConfig(referenceConfig); } async performCleanup(): Promise<void> { await this.storage.performCleanup(); } async dispose(): Promise<void> { return Promise.resolve(this.storage.dispose()); } } /** * Content resolver implementation for dependency injection */ class ContentResolver implements ContentResolverInterface { constructor(private adapter: ContentStorageAdapter) {} async resolveReference( referenceId: string ): Promise<ReferenceResolutionResult> { return await this.adapter.resolveReference(referenceId); } shouldUseReference(content: string | Buffer): boolean { return shouldUseReference(content); } extractReferenceId(input: string): string | null { return extractReferenceId(input); } } /** * Manages content store lifecycle and cross-package registration */ export class ContentStoreManager { private contentStorage: ContentStorage; private adapter: ContentStorageAdapter; private resolver: ContentResolver; private logger: Logger; protected isRegistered = false; constructor( maxMessageStorage: number = 1000, referenceConfig?: Partial<ContentReferenceConfig>, logger?: Logger ) { this.logger = logger || new Logger({ module: 'ContentStoreManager' }); this.contentStorage = new ContentStorage( maxMessageStorage, referenceConfig ); this.adapter = new ContentStorageAdapter(this.contentStorage); this.resolver = new ContentResolver(this.adapter); } /** * Initialize and register content storage for cross-package access */ async initialize(): Promise<void> { if (this.isRegistered) { this.logger.warn('ContentStoreManager is already initialized'); return; } try { if ( ContentStoreService && typeof (ContentStoreService as unknown as { setInstance?: Function }).setInstance === 'function' ) { await (ContentStoreService as unknown as { setInstance: (adapter: unknown) => Promise<void> }).setInstance( this.adapter ); } else { this.logger.warn('ContentStoreService.setInstance is unavailable; skipping registration'); } if ( ContentResolverRegistry && typeof (ContentResolverRegistry as unknown as { register?: Function }).register === 'function' ) { (ContentResolverRegistry as unknown as { register: (resolver: unknown) => void }).register( this.resolver ); } else { this.logger.warn('ContentResolverRegistry.register is unavailable; skipping registration'); } this.isRegistered = true; this.logger.info( 'ContentStoreManager initialized and registered for cross-package access' ); } catch (error) { this.logger.error('Failed to initialize ContentStoreManager:', error); throw error; } } /** * Get the underlying ContentStorage instance */ getContentStorage(): ContentStorage { return this.contentStorage; } /** * Get storage statistics */ async getStats(): Promise<ContentReferenceStats> { return await this.contentStorage.getStats(); } /** * Update configuration */ async updateConfig(config: Partial<ContentReferenceConfig>): Promise<void> { return await this.contentStorage.updateConfig(config); } /** * Perform manual cleanup */ async performCleanup(): Promise<{ cleanedUp: number; errors: string[] }> { return await this.contentStorage.performCleanup(); } /** * Check if content should be stored as reference */ shouldUseReference(content: Buffer | string): boolean { return this.contentStorage.shouldUseReference(content); } /** * Store content if it's large enough */ async storeContentIfLarge( content: Buffer | string, metadata: AdapterContentMetadata ): Promise<ContentReference | null> { const storeMetadata = { source: 'system' as const, contentType: 'binary' as const, ...metadata, }; return await this.contentStorage.storeContentIfLarge( content, storeMetadata ); } /** * Cleanup and unregister */ async dispose(): Promise<void> { if (this.isRegistered) { this.contentStorage.dispose(); if ( ContentStoreService && typeof (ContentStoreService as unknown as { dispose?: Function }).dispose === 'function' ) { (ContentStoreService as unknown as { dispose: () => void }).dispose(); } if ( ContentResolverRegistry && typeof (ContentResolverRegistry as unknown as { unregister?: Function }).unregister === 'function' ) { (ContentResolverRegistry as unknown as { unregister: () => void }).unregister(); } this.isRegistered = false; this.logger.info('ContentStoreManager disposed and unregistered'); } } /** * Check if the manager is initialized */ isInitialized(): boolean { return this.isRegistered; } }