UNPKG

@composio/core

Version:

![Composio Banner](https://github.com/user-attachments/assets/9ba0e9c1-85a4-4b51-ae60-f9fe7992e819)

173 lines (170 loc) 6.04 kB
import "../../chunk-CWQeghP5.mjs"; import { n as logger_default, t as ComposioError } from "../../ComposioError-ChkSdxqU.mjs"; import { n as getFileDataAfterUploadingToS3, t as downloadFileFromS3 } from "../../fileUtils.node-BOZnseMe.mjs"; import { i as transformProperties, n as schemaHasFileDownloadable, r as schemaHasFileUploadable, t as isPlainObject } from "../../FileToolModifier.utils.neutral-BcIR-cVE.mjs"; //#region src/errors/FileModifierErrors.ts const FileModifierErrorCodes = { FILE_UPLOAD_FAILED: "FILE_UPLOAD_FAILED" }; var ComposioFileUploadError = class extends ComposioError { constructor(message = "Failed to upload file", options = {}) { super(message, { ...options, code: FileModifierErrorCodes.FILE_UPLOAD_FAILED, possibleFixes: options.possibleFixes || ["Check if the file exists in the location provided"] }); this.name = "ComposioFileUploadError"; } }; //#endregion //#region src/utils/modifiers/FileToolModifier.node.ts /** * Recursively walks a runtime value and its matching JSON-Schema node, * uploading any string path whose schema node has `file_uploadable: true`. * The function returns a **new** value with all substitutions applied; * nothing is mutated in-place. */ const hydrateFiles = async (value, schema, ctx) => { if (schema?.file_uploadable) { if (typeof value !== "string" && !(value instanceof File)) return value; logger_default.debug(`Uploading file "${value}"`); return getFileDataAfterUploadingToS3(value, { toolSlug: ctx.toolSlug, toolkitSlug: ctx.toolkitSlug, client: ctx.client }); } const schemaVariants = [ ...schema?.anyOf ?? [], ...schema?.oneOf ?? [], ...schema?.allOf ?? [] ]; if (schemaVariants.length > 0) { const uploadableVariants = schemaVariants.filter(schemaHasFileUploadable); if (uploadableVariants.length > 0) { let result = value; for (const variant of uploadableVariants) result = await hydrateFiles(result, variant, ctx); return result; } } if (schema?.type === "object" && schema.properties && isPlainObject(value)) { const transformed = {}; for (const [k, v] of Object.entries(value)) transformed[k] = await hydrateFiles(v, schema.properties[k], ctx); return transformed; } if (schema?.type === "array" && schema.items && Array.isArray(value)) { const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items; return Promise.all(value.map((item) => hydrateFiles(item, itemSchema, ctx))); } return value; }; /** * Downloads a file from S3 and returns a replacement object. */ const downloadS3File = async (value, ctx) => { const { s3url, mimetype } = value; try { logger_default.debug(`Downloading from S3: ${s3url}`); const dl = await downloadFileFromS3({ toolSlug: ctx.toolSlug, s3Url: s3url, mimeType: mimetype ?? "application/octet-stream" }); logger_default.debug(`Downloaded → ${dl.filePath}`); return { uri: dl.filePath, file_downloaded: dl.filePath ? true : false, s3url, mimeType: dl.mimeType }; } catch (err) { logger_default.error(`Download failed: ${s3url}`, { cause: err }); return { uri: "", file_downloaded: false, s3url, mimeType: mimetype ?? "application/octet-stream" }; } }; /** * Recursively walks an arbitrary value and its matching JSON-Schema node. * Whenever it encounters an object that represents a file reference * (i.e. has an `s3url`), it downloads the file and returns a replacement: * { * uri: "<local-path>", * file_downloaded: true | false, * s3url: "<original S3 URL>", * mimeType: "<detected-or-fallback-mime-type>" * } * * The function is side-effect-free: it never mutates the input value. */ const hydrateDownloads = async (value, schema, ctx) => { if (isPlainObject(value) && typeof value.s3url === "string") return downloadS3File(value, ctx); if (schema?.file_downloadable && isPlainObject(value) && typeof value.s3url === "string") return downloadS3File(value, ctx); const schemaVariants = [ ...schema?.anyOf ?? [], ...schema?.oneOf ?? [], ...schema?.allOf ?? [] ]; if (schemaVariants.length > 0) { const downloadableVariants = schemaVariants.filter(schemaHasFileDownloadable); let result = value; for (const variant of downloadableVariants) result = await hydrateDownloads(result, variant, ctx); if (downloadableVariants.length === 0) return hydrateDownloads(value, void 0, ctx); return result; } if (isPlainObject(value)) { const pairs = await Promise.all(Object.entries(value).map(async ([k, v]) => [k, await hydrateDownloads(v, schema?.properties?.[k], ctx)])); return Object.fromEntries(pairs); } if (Array.isArray(value)) { const itemSchema = schema?.items ? Array.isArray(schema.items) ? schema.items[0] : schema.items : void 0; return Promise.all(value.map((item) => hydrateDownloads(item, itemSchema, ctx))); } return value; }; var FileToolModifier = class { client; constructor(client) { this.client = client; } async modifyToolSchema(toolSlug, toolkitSlug, schema) { if (!schema.inputParameters?.properties) return schema; const properties = transformProperties(schema.inputParameters.properties); return { ...schema, inputParameters: { ...schema.inputParameters, properties } }; } async fileUploadModifier(tool, options) { const { params, toolSlug, toolkitSlug = "unknown" } = options; const { arguments: args } = params; if (!args || typeof args !== "object") return params; try { const newArgs = await hydrateFiles(args, tool.inputParameters, { toolSlug, toolkitSlug, client: this.client }); return { ...params, arguments: newArgs }; } catch (error) { throw new ComposioFileUploadError("Failed to upload file", { cause: error }); } } async fileDownloadModifier(tool, options) { const { result, toolSlug } = options; const dataWithDownloads = await hydrateDownloads(result.data, tool.outputParameters, { toolSlug }); return { ...result, data: dataWithDownloads }; } }; //#endregion export { FileToolModifier };