2024-08-14 06:20:02 -07:00
import {
NodeOperationError ,
2024-08-29 06:55:53 -07:00
NodeConnectionType ,
2024-08-14 06:20:02 -07:00
type IExecuteFunctions ,
type INodeExecutionData ,
type INodeType ,
type INodeTypeDescription ,
2024-11-05 10:47:52 -08:00
AI_TRANSFORM_CODE_GENERATED_FOR_PROMPT ,
AI_TRANSFORM_JS_CODE ,
2024-08-14 06:20:02 -07:00
} from 'n8n-workflow' ;
import set from 'lodash/set' ;
import { JavaScriptSandbox } from '../Code/JavaScriptSandbox' ;
import { getSandboxContext } from '../Code/Sandbox' ;
import { standardizeOutput } from '../Code/utils' ;
const { CODE_ENABLE_STDOUT } = process . env ;
export class AiTransform implements INodeType {
description : INodeTypeDescription = {
displayName : 'AI Transform' ,
name : 'aiTransform' ,
icon : 'file:aitransform.svg' ,
group : [ 'transform' ] ,
version : 1 ,
description : 'Modify data based on instructions written in plain english' ,
defaults : {
name : 'AI Transform' ,
} ,
2024-08-29 06:55:53 -07:00
inputs : [ NodeConnectionType . Main ] ,
outputs : [ NodeConnectionType . Main ] ,
2024-08-14 06:20:02 -07:00
parameterPane : 'wide' ,
2024-11-07 04:42:26 -08:00
hints : [
{
message :
"This node doesn't have access to the contents of binary files. To use those contents here, use the 'Extract from File' node first." ,
displayCondition : '={{ $input.all().some(i => i.binary) }}' ,
location : 'outputPane' ,
} ,
] ,
2024-08-14 06:20:02 -07:00
properties : [
{
displayName : 'Instructions' ,
2024-08-14 06:41:35 -07:00
name : 'instructions' ,
2024-08-14 06:20:02 -07:00
type : 'button' ,
default : '' ,
description :
"Provide instructions on how you want to transform the data, then click 'Generate code'. Use dot notation to refer to nested fields (e.g. address.street)." ,
placeholder :
"Example: Merge 'firstname' and 'lastname' into a field 'details.name' and sort by 'email'" ,
typeOptions : {
buttonConfig : {
label : 'Generate code' ,
hasInputField : true ,
inputFieldMaxLength : 500 ,
action : {
type : 'askAiCodeGeneration' ,
2024-11-05 10:47:52 -08:00
target : AI_TRANSFORM_JS_CODE ,
2024-08-14 06:20:02 -07:00
} ,
} ,
} ,
} ,
{
2024-11-05 10:47:52 -08:00
displayName : 'Code Generated For Prompt' ,
name : AI_TRANSFORM_CODE_GENERATED_FOR_PROMPT ,
type : 'hidden' ,
default : '' ,
} ,
{
displayName : 'Generated JavaScript' ,
name : AI_TRANSFORM_JS_CODE ,
2024-08-14 06:20:02 -07:00
type : 'string' ,
typeOptions : {
editor : 'jsEditor' ,
editorIsReadOnly : true ,
} ,
default : '' ,
2024-11-05 10:47:52 -08:00
hint : 'Read-only. To edit this code, adjust the instructions or copy and paste it into a Code node.' ,
2024-08-14 06:20:02 -07:00
noDataExpression : true ,
} ,
] ,
} ;
async execute ( this : IExecuteFunctions ) {
const workflowMode = this . getMode ( ) ;
const node = this . getNode ( ) ;
const codeParameterName = 'jsCode' ;
const getSandbox = ( index = 0 ) = > {
let code = '' ;
try {
code = this . getNodeParameter ( codeParameterName , index ) as string ;
if ( ! code ) {
2024-08-14 06:41:35 -07:00
const instructions = this . getNodeParameter ( 'instructions' , index ) as string ;
2024-08-14 06:20:02 -07:00
if ( ! instructions ) {
throw new NodeOperationError ( node , 'Missing instructions to generate code' , {
description :
"Enter your prompt in the 'Instructions' parameter and click 'Generate code'" ,
} ) ;
}
throw new NodeOperationError ( node , 'Missing code for data transformation' , {
description : "Click the 'Generate code' button to create the code" ,
} ) ;
}
} catch ( error ) {
if ( error instanceof NodeOperationError ) throw error ;
throw new NodeOperationError ( node , error ) ;
}
const context = getSandboxContext . call ( this , index ) ;
context . items = context . $input . all ( ) ;
const Sandbox = JavaScriptSandbox ;
2024-10-07 11:18:32 -07:00
const sandbox = new Sandbox ( context , code , this . helpers ) ;
2024-08-14 06:20:02 -07:00
sandbox . on (
'output' ,
workflowMode === 'manual'
? this . sendMessageToUI
: CODE_ENABLE_STDOUT === 'true'
? ( . . . args ) = >
console . log ( ` [Workflow " ${ this . getWorkflow ( ) . id } "][Node " ${ node . name } "] ` , . . . args )
: ( ) = > { } ,
) ;
return sandbox ;
} ;
const sandbox = getSandbox ( ) ;
let items : INodeExecutionData [ ] ;
try {
items = ( await sandbox . runCodeAllItems ( ) ) as INodeExecutionData [ ] ;
} catch ( error ) {
2024-08-30 00:59:30 -07:00
if ( ! this . continueOnFail ( ) ) {
2024-08-14 06:20:02 -07:00
set ( error , 'node' , node ) ;
throw error ;
}
items = [ { json : { error : error.message } } ] ;
}
for ( const item of items ) {
standardizeOutput ( item . json ) ;
}
return [ items ] ;
}
}