mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
improvement(file-block): add get operation (#4588)
* File block get * Lint * Fix
This commit is contained in:
@@ -8,6 +8,7 @@ import { ensureAbsoluteUrl } from '@/lib/core/utils/urls'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import {
|
||||
fetchWorkspaceFileBuffer,
|
||||
getWorkspaceFile,
|
||||
getWorkspaceFileByName,
|
||||
updateWorkspaceFileContent,
|
||||
uploadWorkspaceFile,
|
||||
@@ -40,6 +41,51 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
|
||||
try {
|
||||
switch (body.operation) {
|
||||
case 'get': {
|
||||
const { fileId, fileInput } = body
|
||||
const selectedFileId =
|
||||
fileId ||
|
||||
(fileInput && typeof fileInput === 'object' && !Array.isArray(fileInput)
|
||||
? typeof fileInput.id === 'string'
|
||||
? fileInput.id
|
||||
: typeof fileInput.fileId === 'string'
|
||||
? fileInput.fileId
|
||||
: ''
|
||||
: '')
|
||||
|
||||
if (!selectedFileId) {
|
||||
return NextResponse.json({ success: false, error: 'File is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const file = await getWorkspaceFile(workspaceId, selectedFileId)
|
||||
if (!file) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: `File not found: "${selectedFileId}"` },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info('File retrieved', {
|
||||
fileId: file.id,
|
||||
name: file.name,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
file: {
|
||||
id: file.id,
|
||||
name: file.name,
|
||||
url: ensureAbsoluteUrl(file.path),
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
key: file.key,
|
||||
context: 'workspace',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
case 'write': {
|
||||
const { fileName, content, contentType } = body
|
||||
const mimeType = contentType || getMimeTypeFromExtension(getFileExtension(fileName))
|
||||
|
||||
@@ -266,6 +266,7 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
|
||||
type: 'dropdown' as SubBlockType,
|
||||
options: [
|
||||
{ label: 'Read', id: 'file_parser_v3' },
|
||||
{ label: 'Get', id: 'file_get' },
|
||||
{ label: 'Write', id: 'file_write' },
|
||||
{ label: 'Append', id: 'file_append' },
|
||||
],
|
||||
@@ -294,6 +295,28 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
|
||||
required: { field: 'operation', value: 'file_parser_v3' },
|
||||
condition: { field: 'operation', value: 'file_parser_v3' },
|
||||
},
|
||||
{
|
||||
id: 'getFile',
|
||||
title: 'File',
|
||||
type: 'file-upload' as SubBlockType,
|
||||
canonicalParamId: 'getFileInput',
|
||||
acceptedTypes: '*',
|
||||
placeholder: 'Select a workspace file',
|
||||
multiple: false,
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: 'file_get' },
|
||||
required: { field: 'operation', value: 'file_get' },
|
||||
},
|
||||
{
|
||||
id: 'getFileId',
|
||||
title: 'File ID',
|
||||
type: 'short-input' as SubBlockType,
|
||||
canonicalParamId: 'getFileInput',
|
||||
placeholder: 'Workspace file ID',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: 'file_get' },
|
||||
required: { field: 'operation', value: 'file_get' },
|
||||
},
|
||||
{
|
||||
id: 'fileName',
|
||||
title: 'File Name',
|
||||
@@ -349,7 +372,7 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['file_parser_v3', 'file_write', 'file_append'],
|
||||
access: ['file_parser_v3', 'file_get', 'file_write', 'file_append'],
|
||||
config: {
|
||||
tool: (params) => params.operation || 'file_parser_v3',
|
||||
params: (params) => {
|
||||
@@ -390,6 +413,25 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'file_get') {
|
||||
const getInput = params.getFileInput
|
||||
if (!getInput) {
|
||||
throw new Error('File is required for get')
|
||||
}
|
||||
|
||||
if (typeof getInput === 'string') {
|
||||
return {
|
||||
fileId: getInput.trim(),
|
||||
workspaceId: params._context?.workspaceId,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
fileInput: normalizeFileInput(getInput, { single: true }),
|
||||
workspaceId: params._context?.workspaceId,
|
||||
}
|
||||
}
|
||||
|
||||
const fileInput = params.fileInput
|
||||
if (!fileInput) {
|
||||
logger.error('No file input provided')
|
||||
@@ -428,9 +470,13 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform (read, write, or append)' },
|
||||
operation: {
|
||||
type: 'string',
|
||||
description: 'Operation to perform (read, get, write, or append)',
|
||||
},
|
||||
fileInput: { type: 'json', description: 'File input for read' },
|
||||
fileType: { type: 'string', description: 'File type for read' },
|
||||
getFileInput: { type: 'json', description: 'Selected file or workspace file ID for get' },
|
||||
fileName: { type: 'string', description: 'Name for a new file (write)' },
|
||||
content: { type: 'string', description: 'File content to write' },
|
||||
contentType: { type: 'string', description: 'MIME content type for write' },
|
||||
@@ -446,6 +492,10 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
|
||||
type: 'string',
|
||||
description: 'All file contents merged into a single text string (read)',
|
||||
},
|
||||
file: {
|
||||
type: 'file',
|
||||
description: 'Workspace file object (get)',
|
||||
},
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'File ID (write)',
|
||||
|
||||
@@ -22,9 +22,21 @@ export const fileManageAppendBodySchema = z.object({
|
||||
content: z.string({ error: 'content is required for append operation' }),
|
||||
})
|
||||
|
||||
export const fileManageBodySchema = z.discriminatedUnion('operation', [
|
||||
export const fileManageGetBodySchema = z
|
||||
.object({
|
||||
operation: z.literal('get'),
|
||||
workspaceId: z.string().min(1).optional(),
|
||||
fileId: z.string().min(1).optional(),
|
||||
fileInput: z.any().optional(),
|
||||
})
|
||||
.refine((data) => data.fileId !== undefined || data.fileInput !== undefined, {
|
||||
message: 'Either fileId or fileInput is required for get operation',
|
||||
})
|
||||
|
||||
export const fileManageBodySchema = z.union([
|
||||
fileManageWriteBodySchema,
|
||||
fileManageAppendBodySchema,
|
||||
fileManageGetBodySchema,
|
||||
])
|
||||
|
||||
export const fileManageContract = defineRouteContract({
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { ToolConfig, ToolResponse, WorkflowToolExecutionContext } from '@/tools/types'
|
||||
|
||||
interface FileGetParams {
|
||||
fileId?: string
|
||||
fileInput?: unknown
|
||||
workspaceId?: string
|
||||
_context?: WorkflowToolExecutionContext
|
||||
}
|
||||
|
||||
export const fileGetTool: ToolConfig<FileGetParams, ToolResponse> = {
|
||||
id: 'file_get',
|
||||
name: 'File Get',
|
||||
description: 'Get a workspace file object from a selected file or canonical workspace file ID.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Canonical workspace file ID.',
|
||||
},
|
||||
fileInput: {
|
||||
type: 'file',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Selected workspace file object.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/file/manage',
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params) => ({
|
||||
operation: 'get',
|
||||
fileId: params.fileId,
|
||||
fileInput: params.fileInput,
|
||||
workspaceId: params.workspaceId || params._context?.workspaceId,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
if (!response.ok || !data.success) {
|
||||
return { success: false, output: {}, error: data.error || 'Failed to get file' }
|
||||
}
|
||||
return { success: true, output: data.data }
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: { type: 'file', description: 'Workspace file object' },
|
||||
},
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { fileParserTool, fileParserV2Tool, fileParserV3Tool } from '@/tools/file/parser'
|
||||
|
||||
export { fileAppendTool } from '@/tools/file/append'
|
||||
export { fileGetTool } from '@/tools/file/get'
|
||||
export { fileWriteTool } from '@/tools/file/write'
|
||||
|
||||
export const fileParseTool = fileParserTool
|
||||
|
||||
@@ -643,6 +643,7 @@ import {
|
||||
} from '@/tools/fathom'
|
||||
import {
|
||||
fileAppendTool,
|
||||
fileGetTool,
|
||||
fileParserV2Tool,
|
||||
fileParserV3Tool,
|
||||
fileParseTool,
|
||||
@@ -3213,6 +3214,7 @@ export const tools: Record<string, ToolConfig> = {
|
||||
file_parser_v2: fileParserV2Tool,
|
||||
file_parser_v3: fileParserV3Tool,
|
||||
file_append: fileAppendTool,
|
||||
file_get: fileGetTool,
|
||||
file_write: fileWriteTool,
|
||||
firecrawl_scrape: firecrawlScrapeTool,
|
||||
firecrawl_search: firecrawlSearchTool,
|
||||
|
||||
Reference in New Issue
Block a user