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

164 lines (141 loc) 4.91 kB
import { BaseHederaQueryTool, HederaAgentKit, type GenericPluginContext, } from "hedera-agent-kit"; import { Bee, FileUploadOptions } from "@ethersphere/bee-js"; import { z } from "zod"; import { promisify } from "util"; import fs from "fs"; import { errorHasStatus, getErrorMessage, getResponseWithStructuredContent, getUploadPostageBatchId, ToolResponse, } from "../utils"; import { BAD_REQUEST_STATUS, DEFAULT_DEFERRED_UPLOAD_SIZE_THRESHOLD_MB, GATEWAY_TAG_ERROR_MESSAGE, NOT_FOUND_STATUS } from "../constants"; import { SwarmConfig } from "../config"; const UploadFileSchema = z.object({ data: z.string(), isPath: z.boolean(), redundancyLevel: z.number().optional(), postageBatchId: z.string().optional(), }); export class UploadFileTool extends BaseHederaQueryTool<typeof UploadFileSchema> { name = "swarm-upload-file"; description = `Upload a file to Swarm. data: base64 encoded file content or file path. isPath: Wether the data parameter is a path. redundancyLevel: Redundancy level for fault tolerance (higher values provide better fault tolerance but increase storage overhead). 0 - none, 1 - medium, 2 - strong, 3 - insane, 4 - paranoid. postageBatchId: The postage stamp batch ID which will be used to perform the upload, if it is provided.`; namespace = "swarm"; specificInputSchema = UploadFileSchema; bee: Bee; config: SwarmConfig; constructor(params: { hederaKit: HederaAgentKit; config: SwarmConfig; logger?: GenericPluginContext['logger']; bee: Bee; }) { const { bee, config, ...rest } = params; super(rest); this.bee = bee; this.config = config; } protected async executeQuery( input: z.infer<typeof UploadFileSchema> ): Promise<ToolResponse | string> { const { data, isPath, redundancyLevel: inputRedundancyLevel, postageBatchId: inputPostageBatchId } = input; if (!data) { this.logger.error( 'Missing required parameter: data.' ); throw new Error('Missing required parameter: data.'); } let postageBatchId = ""; try { postageBatchId = await getUploadPostageBatchId( inputPostageBatchId, this.bee, this.config, ); } catch (error) { let errorMessage = 'Upload file failed.'; if (error instanceof Error) { errorMessage = error.message; } this.logger.error(errorMessage); throw new Error(errorMessage); } let binaryData: Buffer; let name: string | undefined; if (isPath) { // Read file from path try { binaryData = await promisify(fs.readFile)(data); } catch (fileError) { this.logger.error( `Unable to read file at path: ${data}.`, fileError ); throw new Error(`Unable to read file at path: ${data}.`); } name = data.split("/").pop(); } else { binaryData = Buffer.from(data, "base64"); } const redundancyLevel = inputRedundancyLevel; const options: FileUploadOptions = {}; const deferredUploadSizeThreshold = Number(this.config.deferredUploadSizeThresholdMB) || DEFAULT_DEFERRED_UPLOAD_SIZE_THRESHOLD_MB; const deferred = binaryData.length > deferredUploadSizeThreshold * 1024 * 1024; options.deferred = deferred; if (redundancyLevel) { options.redundancyLevel = redundancyLevel; } let message = "File successfully uploaded to Swarm"; let tagId: string | undefined = undefined; // Create tag for deferred uploads or when explicitly requested if (deferred) { try { const tag = await this.bee.createTag(); options.tag = tag.uid; tagId = tag.uid.toString(); message = "File upload started in deferred mode. Use query_upload_progress to track progress."; } catch (error) { if (errorHasStatus(error, NOT_FOUND_STATUS)) { this.logger.error( GATEWAY_TAG_ERROR_MESSAGE, error ); throw new Error(GATEWAY_TAG_ERROR_MESSAGE); } } } let result; try { // Start the deferred upload result = await this.bee.uploadFile(postageBatchId, binaryData, name, options); } catch (error) { let errorMessage = 'Unable to upload file.'; if (errorHasStatus(error, BAD_REQUEST_STATUS)) { errorMessage = getErrorMessage(error); } this.logger.error( errorMessage, error ); throw new Error(errorMessage); } return getResponseWithStructuredContent({ reference: result.reference.toString(), url: this.bee.url + "/bzz/" + result.reference.toString(), message, tagId, }); } }