Skip to content

Commit 214355b

Browse files
authored
improvement(file-block): add get operation (#4588)
* File block get * Lint * Fix
1 parent 4de955d commit 214355b

6 files changed

Lines changed: 168 additions & 3 deletions

File tree

apps/sim/app/api/tools/file/manage/route.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ensureAbsoluteUrl } from '@/lib/core/utils/urls'
88
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
99
import {
1010
fetchWorkspaceFileBuffer,
11+
getWorkspaceFile,
1112
getWorkspaceFileByName,
1213
updateWorkspaceFileContent,
1314
uploadWorkspaceFile,
@@ -40,6 +41,51 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
4041

4142
try {
4243
switch (body.operation) {
44+
case 'get': {
45+
const { fileId, fileInput } = body
46+
const selectedFileId =
47+
fileId ||
48+
(fileInput && typeof fileInput === 'object' && !Array.isArray(fileInput)
49+
? typeof fileInput.id === 'string'
50+
? fileInput.id
51+
: typeof fileInput.fileId === 'string'
52+
? fileInput.fileId
53+
: ''
54+
: '')
55+
56+
if (!selectedFileId) {
57+
return NextResponse.json({ success: false, error: 'File is required' }, { status: 400 })
58+
}
59+
60+
const file = await getWorkspaceFile(workspaceId, selectedFileId)
61+
if (!file) {
62+
return NextResponse.json(
63+
{ success: false, error: `File not found: "${selectedFileId}"` },
64+
{ status: 404 }
65+
)
66+
}
67+
68+
logger.info('File retrieved', {
69+
fileId: file.id,
70+
name: file.name,
71+
})
72+
73+
return NextResponse.json({
74+
success: true,
75+
data: {
76+
file: {
77+
id: file.id,
78+
name: file.name,
79+
url: ensureAbsoluteUrl(file.path),
80+
size: file.size,
81+
type: file.type,
82+
key: file.key,
83+
context: 'workspace',
84+
},
85+
},
86+
})
87+
}
88+
4389
case 'write': {
4490
const { fileName, content, contentType } = body
4591
const mimeType = contentType || getMimeTypeFromExtension(getFileExtension(fileName))

apps/sim/blocks/blocks/file.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
266266
type: 'dropdown' as SubBlockType,
267267
options: [
268268
{ label: 'Read', id: 'file_parser_v3' },
269+
{ label: 'Get', id: 'file_get' },
269270
{ label: 'Write', id: 'file_write' },
270271
{ label: 'Append', id: 'file_append' },
271272
],
@@ -294,6 +295,28 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
294295
required: { field: 'operation', value: 'file_parser_v3' },
295296
condition: { field: 'operation', value: 'file_parser_v3' },
296297
},
298+
{
299+
id: 'getFile',
300+
title: 'File',
301+
type: 'file-upload' as SubBlockType,
302+
canonicalParamId: 'getFileInput',
303+
acceptedTypes: '*',
304+
placeholder: 'Select a workspace file',
305+
multiple: false,
306+
mode: 'basic',
307+
condition: { field: 'operation', value: 'file_get' },
308+
required: { field: 'operation', value: 'file_get' },
309+
},
310+
{
311+
id: 'getFileId',
312+
title: 'File ID',
313+
type: 'short-input' as SubBlockType,
314+
canonicalParamId: 'getFileInput',
315+
placeholder: 'Workspace file ID',
316+
mode: 'advanced',
317+
condition: { field: 'operation', value: 'file_get' },
318+
required: { field: 'operation', value: 'file_get' },
319+
},
297320
{
298321
id: 'fileName',
299322
title: 'File Name',
@@ -349,7 +372,7 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
349372
},
350373
],
351374
tools: {
352-
access: ['file_parser_v3', 'file_write', 'file_append'],
375+
access: ['file_parser_v3', 'file_get', 'file_write', 'file_append'],
353376
config: {
354377
tool: (params) => params.operation || 'file_parser_v3',
355378
params: (params) => {
@@ -390,6 +413,25 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
390413
}
391414
}
392415

416+
if (operation === 'file_get') {
417+
const getInput = params.getFileInput
418+
if (!getInput) {
419+
throw new Error('File is required for get')
420+
}
421+
422+
if (typeof getInput === 'string') {
423+
return {
424+
fileId: getInput.trim(),
425+
workspaceId: params._context?.workspaceId,
426+
}
427+
}
428+
429+
return {
430+
fileInput: normalizeFileInput(getInput, { single: true }),
431+
workspaceId: params._context?.workspaceId,
432+
}
433+
}
434+
393435
const fileInput = params.fileInput
394436
if (!fileInput) {
395437
logger.error('No file input provided')
@@ -428,9 +470,13 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
428470
},
429471
},
430472
inputs: {
431-
operation: { type: 'string', description: 'Operation to perform (read, write, or append)' },
473+
operation: {
474+
type: 'string',
475+
description: 'Operation to perform (read, get, write, or append)',
476+
},
432477
fileInput: { type: 'json', description: 'File input for read' },
433478
fileType: { type: 'string', description: 'File type for read' },
479+
getFileInput: { type: 'json', description: 'Selected file or workspace file ID for get' },
434480
fileName: { type: 'string', description: 'Name for a new file (write)' },
435481
content: { type: 'string', description: 'File content to write' },
436482
contentType: { type: 'string', description: 'MIME content type for write' },
@@ -446,6 +492,10 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
446492
type: 'string',
447493
description: 'All file contents merged into a single text string (read)',
448494
},
495+
file: {
496+
type: 'file',
497+
description: 'Workspace file object (get)',
498+
},
449499
id: {
450500
type: 'string',
451501
description: 'File ID (write)',

apps/sim/lib/api/contracts/tools/file.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,21 @@ export const fileManageAppendBodySchema = z.object({
2222
content: z.string({ error: 'content is required for append operation' }),
2323
})
2424

25-
export const fileManageBodySchema = z.discriminatedUnion('operation', [
25+
export const fileManageGetBodySchema = z
26+
.object({
27+
operation: z.literal('get'),
28+
workspaceId: z.string().min(1).optional(),
29+
fileId: z.string().min(1).optional(),
30+
fileInput: z.any().optional(),
31+
})
32+
.refine((data) => data.fileId !== undefined || data.fileInput !== undefined, {
33+
message: 'Either fileId or fileInput is required for get operation',
34+
})
35+
36+
export const fileManageBodySchema = z.union([
2637
fileManageWriteBodySchema,
2738
fileManageAppendBodySchema,
39+
fileManageGetBodySchema,
2840
])
2941

3042
export const fileManageContract = defineRouteContract({

apps/sim/tools/file/get.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import type { ToolConfig, ToolResponse, WorkflowToolExecutionContext } from '@/tools/types'
2+
3+
interface FileGetParams {
4+
fileId?: string
5+
fileInput?: unknown
6+
workspaceId?: string
7+
_context?: WorkflowToolExecutionContext
8+
}
9+
10+
export const fileGetTool: ToolConfig<FileGetParams, ToolResponse> = {
11+
id: 'file_get',
12+
name: 'File Get',
13+
description: 'Get a workspace file object from a selected file or canonical workspace file ID.',
14+
version: '1.0.0',
15+
16+
params: {
17+
fileId: {
18+
type: 'string',
19+
required: false,
20+
visibility: 'user-or-llm',
21+
description: 'Canonical workspace file ID.',
22+
},
23+
fileInput: {
24+
type: 'file',
25+
required: false,
26+
visibility: 'user-only',
27+
description: 'Selected workspace file object.',
28+
},
29+
},
30+
31+
request: {
32+
url: '/api/tools/file/manage',
33+
method: 'POST',
34+
headers: () => ({ 'Content-Type': 'application/json' }),
35+
body: (params) => ({
36+
operation: 'get',
37+
fileId: params.fileId,
38+
fileInput: params.fileInput,
39+
workspaceId: params.workspaceId || params._context?.workspaceId,
40+
}),
41+
},
42+
43+
transformResponse: async (response) => {
44+
const data = await response.json()
45+
if (!response.ok || !data.success) {
46+
return { success: false, output: {}, error: data.error || 'Failed to get file' }
47+
}
48+
return { success: true, output: data.data }
49+
},
50+
51+
outputs: {
52+
file: { type: 'file', description: 'Workspace file object' },
53+
},
54+
}

apps/sim/tools/file/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fileParserTool, fileParserV2Tool, fileParserV3Tool } from '@/tools/file/parser'
22

33
export { fileAppendTool } from '@/tools/file/append'
4+
export { fileGetTool } from '@/tools/file/get'
45
export { fileWriteTool } from '@/tools/file/write'
56

67
export const fileParseTool = fileParserTool

apps/sim/tools/registry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ import {
643643
} from '@/tools/fathom'
644644
import {
645645
fileAppendTool,
646+
fileGetTool,
646647
fileParserV2Tool,
647648
fileParserV3Tool,
648649
fileParseTool,
@@ -3213,6 +3214,7 @@ export const tools: Record<string, ToolConfig> = {
32133214
file_parser_v2: fileParserV2Tool,
32143215
file_parser_v3: fileParserV3Tool,
32153216
file_append: fileAppendTool,
3217+
file_get: fileGetTool,
32163218
file_write: fileWriteTool,
32173219
firecrawl_scrape: firecrawlScrapeTool,
32183220
firecrawl_search: firecrawlSearchTool,

0 commit comments

Comments
 (0)