1+ import { createLogger } from '@sim/logger'
2+ import { getTableById , queryRows } from '@/lib/table/service'
3+ import {
4+ downloadWorkspaceFile ,
5+ findWorkspaceFileRecord ,
6+ getSandboxWorkspaceFilePath ,
7+ listWorkspaceFiles ,
8+ } from '@/lib/uploads/contexts/workspace/workspace-file-manager'
19import { executeTool as executeAppTool } from '@/tools'
210import type { ToolExecutionContext , ToolExecutionResult } from '../../tool-executor/types'
311
12+ const logger = createLogger ( 'CopilotFunctionExecute' )
13+
14+ const MAX_FILE_SIZE = 10 * 1024 * 1024
15+ const MAX_TOTAL_SIZE = 50 * 1024 * 1024
16+
17+ interface SandboxFile {
18+ path : string
19+ content : string
20+ }
21+
22+ async function resolveInputFiles (
23+ workspaceId : string ,
24+ inputFiles ?: unknown [ ] ,
25+ inputTables ?: unknown [ ]
26+ ) : Promise < SandboxFile [ ] > {
27+ const sandboxFiles : SandboxFile [ ] = [ ]
28+ let totalSize = 0
29+
30+ if ( inputFiles ?. length && workspaceId ) {
31+ const allFiles = await listWorkspaceFiles ( workspaceId )
32+ for ( const fileRef of inputFiles ) {
33+ if ( typeof fileRef !== 'string' ) continue
34+ const record = findWorkspaceFileRecord ( allFiles , fileRef )
35+ if ( ! record ) {
36+ logger . warn ( 'Input file not found' , { fileRef } )
37+ continue
38+ }
39+ if ( record . size > MAX_FILE_SIZE ) {
40+ logger . warn ( 'Input file exceeds size limit' , { fileId : record . id , size : record . size } )
41+ continue
42+ }
43+ if ( totalSize + record . size > MAX_TOTAL_SIZE ) {
44+ logger . warn ( 'Total input size limit reached' )
45+ break
46+ }
47+ const buffer = await downloadWorkspaceFile ( record )
48+ totalSize += buffer . length
49+ const isText = / ^ t e x t \/ | a p p l i c a t i o n \/ j s o n | a p p l i c a t i o n \/ x m l | a p p l i c a t i o n \/ c s v / . test (
50+ record . type || ''
51+ )
52+ const content = isText ? buffer . toString ( 'utf-8' ) : buffer . toString ( 'base64' )
53+ sandboxFiles . push ( {
54+ path : getSandboxWorkspaceFilePath ( record ) ,
55+ content,
56+ encoding : isText ? undefined : 'base64' ,
57+ } as SandboxFile )
58+ }
59+ }
60+
61+ if ( inputTables ?. length ) {
62+ for ( const tableId of inputTables ) {
63+ if ( typeof tableId !== 'string' ) continue
64+ const table = await getTableById ( tableId )
65+ if ( ! table ) {
66+ logger . warn ( 'Input table not found' , { tableId } )
67+ continue
68+ }
69+ const rows = await queryRows ( tableId , workspaceId , { } , 'copilot-fn-exec' )
70+ if ( ! rows . rows ?. length ) continue
71+
72+ const allKeys = new Set < string > ( )
73+ for ( const row of rows . rows ) {
74+ if ( row . data && typeof row . data === 'object' ) {
75+ for ( const key of Object . keys ( row . data as Record < string , unknown > ) ) {
76+ allKeys . add ( key )
77+ }
78+ }
79+ }
80+ const headers = Array . from ( allKeys )
81+ const csvLines = [ headers . join ( ',' ) ]
82+ for ( const row of rows . rows ) {
83+ const data = ( row . data || { } ) as Record < string , unknown >
84+ csvLines . push (
85+ headers
86+ . map ( ( h ) => {
87+ const val = data [ h ]
88+ const str = val === null || val === undefined ? '' : String ( val )
89+ return str . includes ( ',' ) || str . includes ( '"' ) || str . includes ( '\n' )
90+ ? `"${ str . replace ( / " / g, '""' ) } "`
91+ : str
92+ } )
93+ . join ( ',' )
94+ )
95+ }
96+ const csvContent = csvLines . join ( '\n' )
97+ sandboxFiles . push ( {
98+ path : `/home/user/tables/${ tableId } .csv` ,
99+ content : csvContent ,
100+ } )
101+ }
102+ }
103+
104+ return sandboxFiles
105+ }
106+
4107export async function executeFunctionExecute (
5108 params : Record < string , unknown > ,
6109 context : ToolExecutionContext
@@ -14,6 +117,19 @@ export async function executeFunctionExecute(
14117 }
15118 }
16119
120+ if ( context . workspaceId ) {
121+ const inputFiles = enrichedParams . inputFiles as unknown [ ] | undefined
122+ const inputTables = enrichedParams . inputTables as unknown [ ] | undefined
123+
124+ if ( inputFiles ?. length || inputTables ?. length ) {
125+ const resolved = await resolveInputFiles ( context . workspaceId , inputFiles , inputTables )
126+ if ( resolved . length > 0 ) {
127+ const existing = ( enrichedParams . _sandboxFiles as SandboxFile [ ] ) || [ ]
128+ enrichedParams . _sandboxFiles = [ ...existing , ...resolved ]
129+ }
130+ }
131+ }
132+
17133 enrichedParams . _context = {
18134 ...( typeof enrichedParams . _context === 'object' && enrichedParams . _context !== null
19135 ? ( enrichedParams . _context as object )
0 commit comments