@hashgraphonline/conversational-agent
Version:
Hashgraph Online conversational AI agent implementing HCS-10 communication, HCS-2 registries, and content inscription on Hedera. https://hol.org
164 lines (145 loc) • 5.16 kB
text/typescript
import { Logger } from '@hashgraphonline/standards-sdk';
import type { ContentStoreManager as PackageContentStoreManager } from './content-store-manager';
export interface AttachmentData {
name: string;
data: string;
type: string;
size: number;
}
export type ContentStoreManager = PackageContentStoreManager;
/**
* Utility for processing file attachments and content references
*/
export class AttachmentProcessor {
private logger: Logger;
constructor() {
this.logger = new Logger({ module: 'AttachmentProcessor' });
}
/**
* Process attachments and create content references
*/
async processAttachments(
content: string,
attachments: AttachmentData[],
contentStoreManager?: ContentStoreManager
): Promise<string> {
if (attachments.length === 0) {
return content;
}
this.logger.info('Processing attachments with content reference system:', {
attachmentCount: attachments.length,
totalSize: attachments.reduce((sum, att) => sum + att.size, 0),
});
if (contentStoreManager && contentStoreManager.isInitialized()) {
return this.processWithContentStore(content, attachments, contentStoreManager);
} else {
this.logger.warn('Content storage not available, creating simple file references');
return this.processWithSimpleReferences(content, attachments);
}
}
/**
* Process attachments using content store manager
*/
private async processWithContentStore(
content: string,
attachments: AttachmentData[],
contentStoreManager: ContentStoreManager
): Promise<string> {
const contentReferences: string[] = [];
for (const attachment of attachments) {
try {
const base64Data = attachment.data.includes('base64,')
? attachment.data.split('base64,')[1]
: attachment.data;
const buffer = Buffer.from(base64Data, 'base64');
const contentRef = await contentStoreManager.storeContentIfLarge(
buffer,
{
mimeType: attachment.type,
source: 'user_upload',
fileName: attachment.name,
tags: ['attachment', 'user_file'],
}
);
if (contentRef) {
if (attachment.type.startsWith('image/')) {
contentReferences.push(
`[Image File: ${attachment.name}] (content-ref:${contentRef.referenceId})`
);
} else {
contentReferences.push(
`[File: ${attachment.name}] (content-ref:${contentRef.referenceId})`
);
}
} else {
contentReferences.push(
this.createInlineReference(attachment, base64Data)
);
}
} catch (error) {
this.logger.error('Failed to process attachment:', {
fileName: attachment.name,
error: error instanceof Error ? error.message : 'Unknown error',
});
contentReferences.push(
`[File: ${attachment.name} - Error processing file: ${
error instanceof Error ? error.message : 'Unknown error'
}]`
);
}
}
const fileList = this.createFileList(attachments);
return content
? `${content}\n\nAttached files:\n${fileList}\n\n${contentReferences.join('\n')}`
: `Attached files:\n${fileList}\n\n${contentReferences.join('\n')}`;
}
/**
* Process attachments with simple file references
*/
private processWithSimpleReferences(content: string, attachments: AttachmentData[]): string {
const fileReferences = attachments.map((attachment) => {
const sizeStr = this.formatFileSize(attachment.size);
if (attachment.type.startsWith('image/')) {
return `📎 Image: ${attachment.name} (${sizeStr}, ${attachment.type})`;
} else {
return `📎 File: ${attachment.name} (${sizeStr}, ${attachment.type})`;
}
});
return content
? `${content}\n\nAttached files:\n${fileReferences.join('\n')}`
: `Attached files:\n${fileReferences.join('\n')}`;
}
/**
* Create inline reference for small files
*/
private createInlineReference(attachment: AttachmentData, base64Data: string): string {
if (attachment.size < 50000) {
if (attachment.type.startsWith('image/')) {
return ``;
} else {
return `[File: ${attachment.name} (${this.formatFileSize(attachment.size)})]\nContent: ${base64Data}`;
}
} else {
return `[File: ${attachment.name} (${this.formatFileSize(attachment.size)}) - Content too large to include inline]`;
}
}
/**
* Create formatted file list
*/
private createFileList(attachments: AttachmentData[]): string {
return attachments
.map((file) => {
const sizeStr = this.formatFileSize(file.size);
return `📎 ${file.name} (${sizeStr})`;
})
.join('\n');
}
/**
* Format file size for display
*/
private formatFileSize(size: number): string {
return size >= 1024 * 1024
? `${(size / (1024 * 1024)).toFixed(1)}MB`
: `${(size / 1024).toFixed(1)}KB`;
}
}