mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-04 03:35:04 -05:00
normalize file input
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { A2AIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface A2AResponse extends ToolResponse {
|
||||
@@ -214,6 +215,14 @@ export const A2ABlock: BlockConfig<A2AResponse> = {
|
||||
],
|
||||
config: {
|
||||
tool: (params) => params.operation as string,
|
||||
params: (params) => {
|
||||
const { fileUpload, fileReference, ...rest } = params
|
||||
const normalizedFiles = normalizeFileInput(fileUpload || fileReference || params.files)
|
||||
return {
|
||||
...rest,
|
||||
...(normalizedFiles && { files: normalizedFiles }),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ConfluenceIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { ConfluenceResponse } from '@/tools/confluence/types'
|
||||
|
||||
export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
@@ -651,14 +652,15 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
|
||||
|
||||
if (operation === 'upload_attachment') {
|
||||
const fileInput = attachmentFileUpload || attachmentFileReference || attachmentFile
|
||||
if (!fileInput) {
|
||||
const normalizedFile = normalizeFileInput(fileInput)
|
||||
if (!normalizedFile) {
|
||||
throw new Error('File is required for upload attachment operation.')
|
||||
}
|
||||
return {
|
||||
credential,
|
||||
pageId: effectivePageId,
|
||||
operation,
|
||||
file: fileInput,
|
||||
file: normalizedFile,
|
||||
fileName: attachmentFileName,
|
||||
comment: attachmentComment,
|
||||
...rest,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { DropboxIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { DropboxResponse } from '@/tools/dropbox/types'
|
||||
|
||||
export const DropboxBlock: BlockConfig<DropboxResponse> = {
|
||||
@@ -316,6 +317,12 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
|
||||
params.maxResults = Number(params.maxResults)
|
||||
}
|
||||
|
||||
// Normalize file input for upload operation
|
||||
// normalizeFileInput handles JSON stringified values from advanced mode
|
||||
if (params.fileContent) {
|
||||
params.fileContent = normalizeFileInput(params.fileContent)
|
||||
}
|
||||
|
||||
switch (params.operation) {
|
||||
case 'dropbox_upload':
|
||||
return 'dropbox_upload'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { DocumentIcon } from '@/components/icons'
|
||||
import { inferContextFromKey } from '@/lib/uploads/utils/file-utils'
|
||||
import type { BlockConfig, SubBlockType } from '@/blocks/types'
|
||||
import { createVersionedToolSelector } from '@/blocks/utils'
|
||||
import { createVersionedToolSelector, normalizeFileInput } from '@/blocks/utils'
|
||||
import type { FileParserOutput, FileParserV3Output } from '@/tools/file/types'
|
||||
|
||||
const logger = createLogger('FileBlock')
|
||||
@@ -200,7 +200,21 @@ export const FileV2Block: BlockConfig<FileParserOutput> = {
|
||||
throw new Error('File is required')
|
||||
}
|
||||
|
||||
if (typeof fileInput === 'string') {
|
||||
// First, try to normalize as file objects (handles JSON strings from advanced mode)
|
||||
const normalizedFiles = normalizeFileInput(fileInput)
|
||||
if (normalizedFiles) {
|
||||
const filePaths = resolveFilePathsFromInput(normalizedFiles)
|
||||
if (filePaths.length > 0) {
|
||||
return {
|
||||
filePath: filePaths.length === 1 ? filePaths[0] : filePaths,
|
||||
fileType: params.fileType || 'auto',
|
||||
workspaceId: params._context?.workspaceId,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If normalization fails, treat as direct URL string
|
||||
if (typeof fileInput === 'string' && fileInput.trim()) {
|
||||
return {
|
||||
filePath: fileInput.trim(),
|
||||
fileType: params.fileType || 'auto',
|
||||
@@ -208,22 +222,6 @@ export const FileV2Block: BlockConfig<FileParserOutput> = {
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(fileInput) && fileInput.length > 0) {
|
||||
const filePaths = resolveFilePathsFromInput(fileInput)
|
||||
return {
|
||||
filePath: filePaths.length === 1 ? filePaths[0] : filePaths,
|
||||
fileType: params.fileType || 'auto',
|
||||
}
|
||||
}
|
||||
|
||||
const resolvedSingle = resolveFilePathsFromInput(fileInput)
|
||||
if (resolvedSingle.length > 0) {
|
||||
return {
|
||||
filePath: resolvedSingle[0],
|
||||
fileType: params.fileType || 'auto',
|
||||
}
|
||||
}
|
||||
|
||||
logger.error('Invalid file input format')
|
||||
throw new Error('Invalid file input')
|
||||
},
|
||||
@@ -292,7 +290,23 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
|
||||
throw new Error('File input is required')
|
||||
}
|
||||
|
||||
if (typeof fileInput === 'string') {
|
||||
// First, try to normalize as file objects (handles JSON strings from advanced mode)
|
||||
const normalizedFiles = normalizeFileInput(fileInput)
|
||||
if (normalizedFiles) {
|
||||
const filePaths = resolveFilePathsFromInput(normalizedFiles)
|
||||
if (filePaths.length > 0) {
|
||||
return {
|
||||
filePath: filePaths.length === 1 ? filePaths[0] : filePaths,
|
||||
fileType: params.fileType || 'auto',
|
||||
workspaceId: params._context?.workspaceId,
|
||||
workflowId: params._context?.workflowId,
|
||||
executionId: params._context?.executionId,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If normalization fails, treat as direct URL string
|
||||
if (typeof fileInput === 'string' && fileInput.trim()) {
|
||||
return {
|
||||
filePath: fileInput.trim(),
|
||||
fileType: params.fileType || 'auto',
|
||||
@@ -302,36 +316,6 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(fileInput)) {
|
||||
const filePaths = resolveFilePathsFromInput(fileInput)
|
||||
if (filePaths.length === 0) {
|
||||
logger.error('No valid file paths found in file input array')
|
||||
throw new Error('File input is required')
|
||||
}
|
||||
return {
|
||||
filePath: filePaths.length === 1 ? filePaths[0] : filePaths,
|
||||
fileType: params.fileType || 'auto',
|
||||
workspaceId: params._context?.workspaceId,
|
||||
workflowId: params._context?.workflowId,
|
||||
executionId: params._context?.executionId,
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof fileInput === 'object') {
|
||||
const resolvedPaths = resolveFilePathsFromInput(fileInput)
|
||||
if (resolvedPaths.length === 0) {
|
||||
logger.error('File input object missing path, url, or key')
|
||||
throw new Error('File input is required')
|
||||
}
|
||||
return {
|
||||
filePath: resolvedPaths[0],
|
||||
fileType: params.fileType || 'auto',
|
||||
workspaceId: params._context?.workspaceId,
|
||||
workflowId: params._context?.workflowId,
|
||||
executionId: params._context?.executionId,
|
||||
}
|
||||
}
|
||||
|
||||
logger.error('Invalid file input format')
|
||||
throw new Error('File input is required')
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import { FirefliesIcon } from '@/components/icons'
|
||||
import { resolveHttpsUrlFromFileInput } from '@/lib/uploads/utils/file-utils'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { FirefliesResponse } from '@/tools/fireflies/types'
|
||||
import { getTrigger } from '@/triggers'
|
||||
|
||||
@@ -619,26 +620,13 @@ export const FirefliesV2Block: BlockConfig<FirefliesResponse> = {
|
||||
}
|
||||
|
||||
if (params.operation === 'fireflies_upload_audio') {
|
||||
let audioInput = params.audioFile || params.audioFileReference
|
||||
if (!audioInput) {
|
||||
const audioFiles =
|
||||
normalizeFileInput(params.audioFile) || normalizeFileInput(params.audioFileReference)
|
||||
if (!audioFiles || audioFiles.length === 0) {
|
||||
throw new Error('Audio file is required.')
|
||||
}
|
||||
if (typeof audioInput === 'string') {
|
||||
try {
|
||||
audioInput = JSON.parse(audioInput)
|
||||
} catch {
|
||||
throw new Error('Audio file must be a valid file reference.')
|
||||
}
|
||||
}
|
||||
if (Array.isArray(audioInput)) {
|
||||
throw new Error(
|
||||
'File reference must be a single file, not an array. Use <block.files[0]> to select one file.'
|
||||
)
|
||||
}
|
||||
if (typeof audioInput !== 'object' || audioInput === null) {
|
||||
throw new Error('Audio file must be a file reference.')
|
||||
}
|
||||
const audioUrl = resolveHttpsUrlFromFileInput(audioInput)
|
||||
const audioFile = audioFiles[0]
|
||||
const audioUrl = resolveHttpsUrlFromFileInput(audioFile)
|
||||
if (!audioUrl) {
|
||||
throw new Error('Audio file must include a https URL.')
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GmailIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { createVersionedToolSelector } from '@/blocks/utils'
|
||||
import { createVersionedToolSelector, normalizeFileInput } from '@/blocks/utils'
|
||||
import type { GmailToolResponse } from '@/tools/gmail/types'
|
||||
import { getTrigger } from '@/triggers'
|
||||
|
||||
@@ -418,6 +418,8 @@ Return ONLY the search query - no explanations, no extra text.`,
|
||||
labelActionMessageId,
|
||||
labelManagement,
|
||||
manualLabelManagement,
|
||||
attachmentFiles,
|
||||
attachments,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
@@ -465,9 +467,13 @@ Return ONLY the search query - no explanations, no extra text.`,
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize attachments for send/draft operations
|
||||
const normalizedAttachments = normalizeFileInput(attachmentFiles || attachments)
|
||||
|
||||
return {
|
||||
...rest,
|
||||
credential,
|
||||
...(normalizedAttachments && { attachments: normalizedAttachments }),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { GoogleDriveIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { GoogleDriveResponse } from '@/tools/google_drive/types'
|
||||
|
||||
export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
@@ -782,6 +783,8 @@ Return ONLY the message text - no subject line, no greetings/signatures, no extr
|
||||
manualDestinationFolderId,
|
||||
fileSelector,
|
||||
manualFileId,
|
||||
file,
|
||||
fileUpload,
|
||||
mimeType,
|
||||
shareType,
|
||||
starred,
|
||||
@@ -789,6 +792,9 @@ Return ONLY the message text - no subject line, no greetings/signatures, no extr
|
||||
...rest
|
||||
} = params
|
||||
|
||||
// Normalize file input - handles both basic (file-upload) and advanced (short-input) modes
|
||||
const normalizedFile = normalizeFileInput(file ?? fileUpload)
|
||||
|
||||
// Use folderSelector if provided, otherwise use manualFolderId
|
||||
const effectiveFolderId = (folderSelector || manualFolderId || '').trim()
|
||||
|
||||
@@ -813,6 +819,7 @@ Return ONLY the message text - no subject line, no greetings/signatures, no extr
|
||||
folderId: effectiveFolderId || undefined,
|
||||
fileId: effectiveFileId || undefined,
|
||||
destinationFolderId: effectiveDestinationFolderId || undefined,
|
||||
file: normalizedFile,
|
||||
pageSize: rest.pageSize ? Number.parseInt(rest.pageSize as string, 10) : undefined,
|
||||
mimeType: mimeType,
|
||||
type: shareType, // Map shareType to type for share tool
|
||||
|
||||
@@ -2,6 +2,7 @@ import { GoogleSlidesIcon } from '@/components/icons'
|
||||
import { resolveHttpsUrlFromFileInput } from '@/lib/uploads/utils/file-utils'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { GoogleSlidesResponse } from '@/tools/google_slides/types'
|
||||
|
||||
export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
|
||||
@@ -960,26 +961,18 @@ export const GoogleSlidesV2Block: BlockConfig<GoogleSlidesResponse> = {
|
||||
}
|
||||
|
||||
if (params.operation === 'add_image') {
|
||||
let imageInput = params.imageFile || params.imageFileReference || params.imageSource
|
||||
if (!imageInput) {
|
||||
const imageInput = params.imageFile || params.imageFileReference || params.imageSource
|
||||
const normalizedFiles = normalizeFileInput(imageInput)
|
||||
if (!normalizedFiles || normalizedFiles.length === 0) {
|
||||
throw new Error('Image file is required.')
|
||||
}
|
||||
if (typeof imageInput === 'string') {
|
||||
try {
|
||||
imageInput = JSON.parse(imageInput)
|
||||
} catch {
|
||||
throw new Error('Image file must be a valid file reference.')
|
||||
}
|
||||
}
|
||||
if (Array.isArray(imageInput)) {
|
||||
if (normalizedFiles.length > 1) {
|
||||
throw new Error(
|
||||
'File reference must be a single file, not an array. Use <block.files[0]> to select one file.'
|
||||
)
|
||||
}
|
||||
if (typeof imageInput !== 'object' || imageInput === null) {
|
||||
throw new Error('Image file must be a file reference.')
|
||||
}
|
||||
const imageUrl = resolveHttpsUrlFromFileInput(imageInput)
|
||||
const fileObject = normalizedFiles[0]
|
||||
const imageUrl = resolveHttpsUrlFromFileInput(fileObject)
|
||||
if (!imageUrl) {
|
||||
throw new Error('Image file must include a https URL.')
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { LinearResponse } from '@/tools/linear/types'
|
||||
import { getTrigger } from '@/triggers'
|
||||
|
||||
@@ -1773,16 +1774,21 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
if (!params.issueId?.trim()) {
|
||||
throw new Error('Issue ID is required.')
|
||||
}
|
||||
if (Array.isArray(params.file)) {
|
||||
// Normalize file inputs - handles JSON stringified values from advanced mode
|
||||
const normalizedUpload = normalizeFileInput(params.attachmentFileUpload)
|
||||
const normalizedFile = normalizeFileInput(params.file)
|
||||
// Take the first file from whichever input has data (Linear only accepts single file)
|
||||
const attachmentFile = normalizedUpload?.[0] || normalizedFile?.[0]
|
||||
// Check for multiple files
|
||||
if (
|
||||
(normalizedUpload && normalizedUpload.length > 1) ||
|
||||
(normalizedFile && normalizedFile.length > 1)
|
||||
) {
|
||||
throw new Error('Attachment file must be a single file.')
|
||||
}
|
||||
if (Array.isArray(params.attachmentFileUpload)) {
|
||||
throw new Error('Attachment file must be a single file.')
|
||||
}
|
||||
const attachmentFile = params.attachmentFileUpload || params.file
|
||||
const attachmentUrl =
|
||||
params.url?.trim() ||
|
||||
(attachmentFile && !Array.isArray(attachmentFile) ? attachmentFile.url : undefined)
|
||||
(attachmentFile ? (attachmentFile as { url?: string }).url : undefined)
|
||||
if (!attachmentUrl) {
|
||||
throw new Error('URL or file is required.')
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MistralIcon } from '@/components/icons'
|
||||
import { AuthMode, type BlockConfig, type SubBlockType } from '@/blocks/types'
|
||||
import { createVersionedToolSelector } from '@/blocks/utils'
|
||||
import { createVersionedToolSelector, normalizeFileInput } from '@/blocks/utils'
|
||||
import type { MistralParserOutput } from '@/tools/mistral/types'
|
||||
|
||||
export const MistralParseBlock: BlockConfig<MistralParserOutput> = {
|
||||
@@ -213,26 +213,18 @@ export const MistralParseV2Block: BlockConfig<MistralParserOutput> = {
|
||||
resultType: params.resultType || 'markdown',
|
||||
}
|
||||
|
||||
let documentInput = params.fileUpload || params.fileReference || params.document
|
||||
if (!documentInput) {
|
||||
const documentInput = normalizeFileInput(
|
||||
params.fileUpload || params.fileReference || params.document
|
||||
)
|
||||
if (!documentInput || documentInput.length === 0) {
|
||||
throw new Error('PDF document is required')
|
||||
}
|
||||
if (typeof documentInput === 'string') {
|
||||
try {
|
||||
documentInput = JSON.parse(documentInput)
|
||||
} catch {
|
||||
throw new Error('PDF document must be a valid file reference')
|
||||
}
|
||||
}
|
||||
if (Array.isArray(documentInput)) {
|
||||
if (documentInput.length > 1) {
|
||||
throw new Error(
|
||||
'File reference must be a single file, not an array. Use <block.attachments[0]> to select one file.'
|
||||
)
|
||||
}
|
||||
if (typeof documentInput !== 'object' || documentInput === null) {
|
||||
throw new Error('PDF document must be a file reference')
|
||||
}
|
||||
parameters.file = documentInput
|
||||
parameters.file = documentInput[0]
|
||||
|
||||
let pagesArray: number[] | undefined
|
||||
if (params.pages && params.pages.trim() !== '') {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { MicrosoftOneDriveIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { OneDriveResponse } from '@/tools/onedrive/types'
|
||||
import { normalizeExcelValuesForToolParams } from '@/tools/onedrive/utils'
|
||||
|
||||
@@ -352,17 +353,31 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, folderId, fileId, mimeType, values, downloadFileName, ...rest } = params
|
||||
const {
|
||||
credential,
|
||||
folderId,
|
||||
fileId,
|
||||
mimeType,
|
||||
values,
|
||||
downloadFileName,
|
||||
file,
|
||||
fileReference,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
let normalizedValues: ReturnType<typeof normalizeExcelValuesForToolParams>
|
||||
if (values !== undefined) {
|
||||
normalizedValues = normalizeExcelValuesForToolParams(values)
|
||||
}
|
||||
|
||||
// Normalize file input from both basic (file-upload) and advanced (short-input) modes
|
||||
const normalizedFile = normalizeFileInput(file || fileReference)
|
||||
|
||||
return {
|
||||
credential,
|
||||
...rest,
|
||||
values: normalizedValues,
|
||||
file: normalizedFile,
|
||||
folderId: folderId || undefined,
|
||||
fileId: fileId || undefined,
|
||||
pageSize: rest.pageSize ? Number.parseInt(rest.pageSize as string, 10) : undefined,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { OutlookIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { OutlookResponse } from '@/tools/outlook/types'
|
||||
import { getTrigger } from '@/triggers'
|
||||
|
||||
@@ -335,12 +336,20 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
copyMessageId,
|
||||
copyDestinationFolder,
|
||||
manualCopyDestinationFolder,
|
||||
attachmentFiles,
|
||||
attachments,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
// Handle both selector and manual folder input
|
||||
const effectiveFolder = (folder || manualFolder || '').trim()
|
||||
|
||||
// Normalize file attachments from either basic (file-upload) or advanced (short-input) mode
|
||||
const normalizedAttachments = normalizeFileInput(attachmentFiles || attachments)
|
||||
if (normalizedAttachments) {
|
||||
rest.attachments = normalizedAttachments
|
||||
}
|
||||
|
||||
if (rest.operation === 'read_outlook') {
|
||||
rest.folder = effectiveFolder || 'INBOX'
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PulseIcon } from '@/components/icons'
|
||||
import { AuthMode, type BlockConfig, type SubBlockType } from '@/blocks/types'
|
||||
import { createVersionedToolSelector } from '@/blocks/utils'
|
||||
import { createVersionedToolSelector, normalizeFileInput } from '@/blocks/utils'
|
||||
import type { PulseParserOutput } from '@/tools/pulse/types'
|
||||
|
||||
export const PulseBlock: BlockConfig<PulseParserOutput> = {
|
||||
@@ -178,26 +178,16 @@ export const PulseV2Block: BlockConfig<PulseParserOutput> = {
|
||||
apiKey: params.apiKey.trim(),
|
||||
}
|
||||
|
||||
let documentInput = params.fileUpload || params.document
|
||||
if (!documentInput) {
|
||||
const normalizedFiles = normalizeFileInput(params.fileUpload || params.document)
|
||||
if (!normalizedFiles || normalizedFiles.length === 0) {
|
||||
throw new Error('Document file is required')
|
||||
}
|
||||
if (typeof documentInput === 'string') {
|
||||
try {
|
||||
documentInput = JSON.parse(documentInput)
|
||||
} catch {
|
||||
throw new Error('Document file must be a valid file reference')
|
||||
}
|
||||
}
|
||||
if (Array.isArray(documentInput)) {
|
||||
if (normalizedFiles.length > 1) {
|
||||
throw new Error(
|
||||
'File reference must be a single file, not an array. Use <block.attachments[0]> to select one file.'
|
||||
)
|
||||
}
|
||||
if (typeof documentInput !== 'object' || documentInput === null) {
|
||||
throw new Error('Document file must be a file reference')
|
||||
}
|
||||
parameters.file = documentInput
|
||||
parameters.file = normalizedFiles[0]
|
||||
|
||||
if (params.pages && params.pages.trim() !== '') {
|
||||
parameters.pages = params.pages.trim()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ReductoIcon } from '@/components/icons'
|
||||
import { AuthMode, type BlockConfig, type SubBlockType } from '@/blocks/types'
|
||||
import { createVersionedToolSelector } from '@/blocks/utils'
|
||||
import { createVersionedToolSelector, normalizeFileInput } from '@/blocks/utils'
|
||||
import type { ReductoParserOutput } from '@/tools/reducto/types'
|
||||
|
||||
export const ReductoBlock: BlockConfig<ReductoParserOutput> = {
|
||||
@@ -182,26 +182,16 @@ export const ReductoV2Block: BlockConfig<ReductoParserOutput> = {
|
||||
apiKey: params.apiKey.trim(),
|
||||
}
|
||||
|
||||
let documentInput = params.fileUpload || params.document
|
||||
if (!documentInput) {
|
||||
const documentInput = normalizeFileInput(params.fileUpload || params.document)
|
||||
if (!documentInput || documentInput.length === 0) {
|
||||
throw new Error('PDF document file is required')
|
||||
}
|
||||
if (typeof documentInput === 'string') {
|
||||
try {
|
||||
documentInput = JSON.parse(documentInput)
|
||||
} catch {
|
||||
throw new Error('PDF document file must be a valid file reference')
|
||||
}
|
||||
}
|
||||
if (Array.isArray(documentInput)) {
|
||||
if (documentInput.length > 1) {
|
||||
throw new Error(
|
||||
'File reference must be a single file, not an array. Use <block.attachments[0]> to select one file.'
|
||||
)
|
||||
}
|
||||
if (typeof documentInput !== 'object' || documentInput === null) {
|
||||
throw new Error('PDF document file must be a file reference')
|
||||
}
|
||||
parameters.file = documentInput
|
||||
parameters.file = documentInput[0]
|
||||
|
||||
let pagesArray: number[] | undefined
|
||||
if (params.pages && params.pages.trim() !== '') {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { S3Icon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { S3Response } from '@/tools/s3/types'
|
||||
|
||||
export const S3Block: BlockConfig<S3Response> = {
|
||||
@@ -271,7 +272,8 @@ export const S3Block: BlockConfig<S3Response> = {
|
||||
throw new Error('Object Key is required for upload')
|
||||
}
|
||||
// Use file from uploadFile if in basic mode, otherwise use file reference
|
||||
const fileParam = params.uploadFile || params.file
|
||||
// normalizeFileInput handles JSON stringified values from advanced mode
|
||||
const fileParam = normalizeFileInput(params.uploadFile || params.file)
|
||||
|
||||
return {
|
||||
accessKeyId: params.accessKeyId,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SendgridIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { SendMailResult } from '@/tools/sendgrid/types'
|
||||
|
||||
export const SendGridBlock: BlockConfig<SendMailResult> = {
|
||||
@@ -561,9 +562,14 @@ Return ONLY the HTML content.`,
|
||||
templateGenerations,
|
||||
listPageSize,
|
||||
templatePageSize,
|
||||
attachmentFiles,
|
||||
attachments,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
// Normalize attachments for send_mail operation
|
||||
const normalizedAttachments = normalizeFileInput(attachmentFiles || attachments)
|
||||
|
||||
// Map renamed fields back to tool parameter names
|
||||
return {
|
||||
...rest,
|
||||
@@ -577,6 +583,7 @@ Return ONLY the HTML content.`,
|
||||
...(templateGenerations && { generations: templateGenerations }),
|
||||
...(listPageSize && { pageSize: listPageSize }),
|
||||
...(templatePageSize && { pageSize: templatePageSize }),
|
||||
...(normalizedAttachments && { attachments: normalizedAttachments }),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { SftpIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { SftpUploadResult } from '@/tools/sftp/types'
|
||||
|
||||
export const SftpBlock: BlockConfig<SftpUploadResult> = {
|
||||
@@ -222,7 +223,7 @@ export const SftpBlock: BlockConfig<SftpUploadResult> = {
|
||||
return {
|
||||
...connectionConfig,
|
||||
remotePath: params.remotePath,
|
||||
files: params.files,
|
||||
files: normalizeFileInput(params.uploadFiles || params.files),
|
||||
overwrite: params.overwrite !== false,
|
||||
permissions: params.permissions,
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { MicrosoftSharepointIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { SharepointResponse } from '@/tools/sharepoint/types'
|
||||
|
||||
const logger = createLogger('SharepointBlock')
|
||||
@@ -449,7 +450,7 @@ Return ONLY the JSON object - no explanations, no markdown, no extra text.`,
|
||||
}
|
||||
|
||||
// Handle file upload files parameter
|
||||
const fileParam = uploadFiles || files
|
||||
const normalizedFiles = normalizeFileInput(uploadFiles || files)
|
||||
const baseParams: Record<string, any> = {
|
||||
credential,
|
||||
siteId: effectiveSiteId || undefined,
|
||||
@@ -463,8 +464,8 @@ Return ONLY the JSON object - no explanations, no markdown, no extra text.`,
|
||||
}
|
||||
|
||||
// Add files if provided
|
||||
if (fileParam) {
|
||||
baseParams.files = fileParam
|
||||
if (normalizedFiles) {
|
||||
baseParams.files = normalizedFiles
|
||||
}
|
||||
|
||||
if (columnDefinitions) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { SmtpIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { SmtpSendMailResult } from '@/tools/smtp/types'
|
||||
|
||||
export const SmtpBlock: BlockConfig<SmtpSendMailResult> = {
|
||||
@@ -176,7 +177,7 @@ export const SmtpBlock: BlockConfig<SmtpSendMailResult> = {
|
||||
cc: params.cc,
|
||||
bcc: params.bcc,
|
||||
replyTo: params.replyTo,
|
||||
attachments: params.attachments,
|
||||
attachments: normalizeFileInput(params.attachmentFiles || params.attachments),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { SpotifyIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export const SpotifyBlock: BlockConfig<ToolResponse> = {
|
||||
@@ -785,6 +786,10 @@ export const SpotifyBlock: BlockConfig<ToolResponse> = {
|
||||
if (params.playUris) {
|
||||
params.uris = params.playUris
|
||||
}
|
||||
// Normalize file input for cover image
|
||||
if (params.coverImage !== undefined) {
|
||||
params.coverImage = normalizeFileInput(params.coverImage)
|
||||
}
|
||||
return params.operation || 'spotify_search'
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { STTIcon } from '@/components/icons'
|
||||
import { AuthMode, type BlockConfig } from '@/blocks/types'
|
||||
import { createVersionedToolSelector } from '@/blocks/utils'
|
||||
import { createVersionedToolSelector, normalizeFileInput } from '@/blocks/utils'
|
||||
import type { SttBlockResponse } from '@/tools/stt/types'
|
||||
|
||||
export const SttBlock: BlockConfig<SttBlockResponse> = {
|
||||
@@ -258,22 +258,28 @@ export const SttBlock: BlockConfig<SttBlockResponse> = {
|
||||
return 'stt_whisper'
|
||||
}
|
||||
},
|
||||
params: (params) => ({
|
||||
provider: params.provider,
|
||||
apiKey: params.apiKey,
|
||||
model: params.model,
|
||||
audioFile: params.audioFile,
|
||||
audioFileReference: params.audioFileReference,
|
||||
audioUrl: params.audioUrl,
|
||||
language: params.language,
|
||||
timestamps: params.timestamps,
|
||||
diarization: params.diarization,
|
||||
translateToEnglish: params.translateToEnglish,
|
||||
sentiment: params.sentiment,
|
||||
entityDetection: params.entityDetection,
|
||||
piiRedaction: params.piiRedaction,
|
||||
summarization: params.summarization,
|
||||
}),
|
||||
params: (params) => {
|
||||
// Normalize file input from basic (file-upload) or advanced (short-input) mode
|
||||
const normalizedFiles = normalizeFileInput(params.audioFile || params.audioFileReference)
|
||||
const audioFile = normalizedFiles?.[0]
|
||||
|
||||
return {
|
||||
provider: params.provider,
|
||||
apiKey: params.apiKey,
|
||||
model: params.model,
|
||||
audioFile,
|
||||
audioFileReference: undefined,
|
||||
audioUrl: params.audioUrl,
|
||||
language: params.language,
|
||||
timestamps: params.timestamps,
|
||||
diarization: params.diarization,
|
||||
translateToEnglish: params.translateToEnglish,
|
||||
sentiment: params.sentiment,
|
||||
entityDetection: params.entityDetection,
|
||||
piiRedaction: params.piiRedaction,
|
||||
summarization: params.summarization,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -386,24 +392,15 @@ export const SttV2Block: BlockConfig<SttBlockResponse> = {
|
||||
fallbackToolId: 'stt_whisper_v2',
|
||||
}),
|
||||
params: (params) => {
|
||||
let audioInput = params.audioFile || params.audioFileReference
|
||||
if (audioInput && typeof audioInput === 'string') {
|
||||
try {
|
||||
audioInput = JSON.parse(audioInput)
|
||||
} catch {
|
||||
throw new Error('Audio file must be a valid file reference')
|
||||
}
|
||||
}
|
||||
if (audioInput && Array.isArray(audioInput)) {
|
||||
throw new Error(
|
||||
'File reference must be a single file, not an array. Use <block.files[0]> to select one file.'
|
||||
)
|
||||
}
|
||||
// Normalize file input from basic (file-upload) or advanced (short-input) mode
|
||||
const normalizedFiles = normalizeFileInput(params.audioFile || params.audioFileReference)
|
||||
const audioFile = normalizedFiles?.[0]
|
||||
|
||||
return {
|
||||
provider: params.provider,
|
||||
apiKey: params.apiKey,
|
||||
model: params.model,
|
||||
audioFile: audioInput,
|
||||
audioFile,
|
||||
audioFileReference: undefined,
|
||||
language: params.language,
|
||||
timestamps: params.timestamps,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { SupabaseIcon } from '@/components/icons'
|
||||
import { AuthMode, type BlockConfig } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { SupabaseResponse } from '@/tools/supabase/types'
|
||||
|
||||
const logger = createLogger('SupabaseBlock')
|
||||
@@ -973,9 +974,16 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
allowedMimeTypes,
|
||||
upsert,
|
||||
download,
|
||||
file,
|
||||
fileContent,
|
||||
fileData,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
// Normalize file input for storage_upload operation
|
||||
// normalizeFileInput handles JSON stringified values from advanced mode
|
||||
const normalizedFileData = normalizeFileInput(file || fileContent || fileData)
|
||||
|
||||
// Parse JSON data if it's a string
|
||||
let parsedData
|
||||
if (data && typeof data === 'string' && data.trim()) {
|
||||
@@ -1102,6 +1110,10 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
|
||||
result.isPublic = parsedIsPublic
|
||||
}
|
||||
|
||||
if (normalizedFileData !== undefined) {
|
||||
result.fileData = normalizedFileData
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
},
|
||||
|
||||
@@ -269,46 +269,46 @@ export const TelegramBlock: BlockConfig<TelegramResponse> = {
|
||||
messageId: params.messageId,
|
||||
}
|
||||
case 'telegram_send_photo': {
|
||||
const photoSource = params.photoFile || params.photo
|
||||
if (!photoSource) {
|
||||
const photoSource = normalizeFileInput(params.photoFile || params.photo)
|
||||
if (!photoSource || photoSource.length === 0) {
|
||||
throw new Error('Photo is required.')
|
||||
}
|
||||
return {
|
||||
...commonParams,
|
||||
photo: photoSource,
|
||||
photo: photoSource[0],
|
||||
caption: params.caption,
|
||||
}
|
||||
}
|
||||
case 'telegram_send_video': {
|
||||
const videoSource = params.videoFile || params.video
|
||||
if (!videoSource) {
|
||||
const videoSource = normalizeFileInput(params.videoFile || params.video)
|
||||
if (!videoSource || videoSource.length === 0) {
|
||||
throw new Error('Video is required.')
|
||||
}
|
||||
return {
|
||||
...commonParams,
|
||||
video: videoSource,
|
||||
video: videoSource[0],
|
||||
caption: params.caption,
|
||||
}
|
||||
}
|
||||
case 'telegram_send_audio': {
|
||||
const audioSource = params.audioFile || params.audio
|
||||
if (!audioSource) {
|
||||
const audioSource = normalizeFileInput(params.audioFile || params.audio)
|
||||
if (!audioSource || audioSource.length === 0) {
|
||||
throw new Error('Audio is required.')
|
||||
}
|
||||
return {
|
||||
...commonParams,
|
||||
audio: audioSource,
|
||||
audio: audioSource[0],
|
||||
caption: params.caption,
|
||||
}
|
||||
}
|
||||
case 'telegram_send_animation': {
|
||||
const animationSource = params.animationFile || params.animation
|
||||
if (!animationSource) {
|
||||
const animationSource = normalizeFileInput(params.animationFile || params.animation)
|
||||
if (!animationSource || animationSource.length === 0) {
|
||||
throw new Error('Animation is required.')
|
||||
}
|
||||
return {
|
||||
...commonParams,
|
||||
animation: animationSource,
|
||||
animation: animationSource[0],
|
||||
caption: params.caption,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TextractIcon } from '@/components/icons'
|
||||
import { AuthMode, type BlockConfig, type SubBlockType } from '@/blocks/types'
|
||||
import { createVersionedToolSelector } from '@/blocks/utils'
|
||||
import { createVersionedToolSelector, normalizeFileInput } from '@/blocks/utils'
|
||||
import type { TextractParserOutput } from '@/tools/textract/types'
|
||||
|
||||
export const TextractBlock: BlockConfig<TextractParserOutput> = {
|
||||
@@ -260,26 +260,11 @@ export const TextractV2Block: BlockConfig<TextractParserOutput> = {
|
||||
}
|
||||
parameters.s3Uri = params.s3Uri.trim()
|
||||
} else {
|
||||
let documentInput = params.fileUpload || params.document
|
||||
if (!documentInput) {
|
||||
const files = normalizeFileInput(params.fileUpload || params.document)
|
||||
if (!files || files.length === 0) {
|
||||
throw new Error('Document file is required')
|
||||
}
|
||||
if (typeof documentInput === 'string') {
|
||||
try {
|
||||
documentInput = JSON.parse(documentInput)
|
||||
} catch {
|
||||
throw new Error('Document file must be a valid file reference')
|
||||
}
|
||||
}
|
||||
if (Array.isArray(documentInput)) {
|
||||
throw new Error(
|
||||
'File reference must be a single file, not an array. Use <block.attachments[0]> to select one file.'
|
||||
)
|
||||
}
|
||||
if (typeof documentInput !== 'object' || documentInput === null) {
|
||||
throw new Error('Document file must be a file reference')
|
||||
}
|
||||
parameters.file = documentInput
|
||||
parameters.file = files[0]
|
||||
}
|
||||
|
||||
const featureTypes: string[] = []
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { VideoIcon } from '@/components/icons'
|
||||
import { AuthMode, type BlockConfig } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { VideoBlockResponse } from '@/tools/video/types'
|
||||
|
||||
export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
|
||||
@@ -745,7 +746,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
|
||||
duration: params.duration ? Number(params.duration) : undefined,
|
||||
aspectRatio: params.aspectRatio,
|
||||
resolution: params.resolution,
|
||||
visualReference: visualRef,
|
||||
visualReference: normalizeFileInput(visualRef),
|
||||
consistencyMode: params.consistencyMode,
|
||||
stylePreset: params.stylePreset,
|
||||
promptOptimizer: params.promptOptimizer,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EyeIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { createVersionedToolSelector } from '@/blocks/utils'
|
||||
import { createVersionedToolSelector, normalizeFileInput } from '@/blocks/utils'
|
||||
import type { VisionResponse } from '@/tools/vision/types'
|
||||
|
||||
const VISION_MODEL_OPTIONS = [
|
||||
@@ -117,22 +117,13 @@ export const VisionV2Block: BlockConfig<VisionResponse> = {
|
||||
fallbackToolId: 'vision_tool_v2',
|
||||
}),
|
||||
params: (params) => {
|
||||
let imageInput = params.imageFile || params.imageFileReference
|
||||
if (imageInput && typeof imageInput === 'string') {
|
||||
try {
|
||||
imageInput = JSON.parse(imageInput)
|
||||
} catch {
|
||||
throw new Error('Image file must be a valid file reference')
|
||||
}
|
||||
}
|
||||
if (imageInput && Array.isArray(imageInput)) {
|
||||
throw new Error(
|
||||
'File reference must be a single file, not an array. Use <block.files[0]> to select one file.'
|
||||
)
|
||||
}
|
||||
// normalizeFileInput handles JSON stringified values from advanced mode
|
||||
const normalizedFiles = normalizeFileInput(params.imageFile || params.imageFileReference)
|
||||
// Vision expects a single file, take the first from the normalized array
|
||||
const imageFile = normalizedFiles?.[0]
|
||||
return {
|
||||
...params,
|
||||
imageFile: imageInput,
|
||||
imageFile,
|
||||
imageFileReference: undefined,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { WordpressIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { WordPressResponse } from '@/tools/wordpress/types'
|
||||
|
||||
export const WordPressBlock: BlockConfig<WordPressResponse> = {
|
||||
@@ -769,7 +770,7 @@ export const WordPressBlock: BlockConfig<WordPressResponse> = {
|
||||
case 'wordpress_upload_media':
|
||||
return {
|
||||
...baseParams,
|
||||
file: params.fileUpload || params.file,
|
||||
file: normalizeFileInput(params.fileUpload || params.file),
|
||||
filename: params.filename,
|
||||
title: params.mediaTitle,
|
||||
caption: params.caption,
|
||||
|
||||
Reference in New Issue
Block a user