setup for local testing

This commit is contained in:
Michael Kret 2024-10-28 11:39:38 +02:00
parent cb7c4d29a6
commit 6b4959fc76
3 changed files with 102 additions and 3 deletions

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ApplicationError, type INodeProperties, type NodePropertyAction } from 'n8n-workflow';
import { type IDataObject, type INodeProperties, type NodePropertyAction } from 'n8n-workflow';
import type { INodeUi, IUpdateInformation } from '@/Interface';
import { ref, computed, onMounted } from 'vue';
import { N8nButton, N8nInput, N8nTooltip } from 'n8n-design-system/components';
@ -10,6 +10,7 @@ import { getSchemas, getParentNodes } from './utils';
import { useRootStore } from '@/stores/root.store';
import { useTelemetry } from '@/composables/useTelemetry';
import { generateCodeForPrompt } from '@/api/ai';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { format } from 'prettier';
import jsParser from 'prettier/plugins/babel';
@ -29,6 +30,7 @@ const props = defineProps<{
const rootStore = useRootStore();
const settingsStore = useSettingsStore();
const nodeTypesStore = useNodeTypesStore();
const i18n = useI18n();
@ -88,7 +90,7 @@ async function onSubmit() {
value: prompt.value,
});
const { type, target } = action;
const { type, handler, target } = action;
startLoading();
@ -113,7 +115,20 @@ async function onSubmit() {
const { code } = await generateCodeForPrompt(restApiContext, payload);
value = code;
} else {
throw new ApplicationError('AI code generation is not enabled');
if (handler) {
const currentNodeParameters = activeNode.parameters;
value = await nodeTypesStore.getNodeParameterActionResult({
nodeTypeAndVersion: {
name: activeNode.type,
version: activeNode.typeVersion,
},
path: props.path,
currentNodeParameters,
credentials: activeNode.credentials,
handler,
payload: payload as unknown as IDataObject,
});
}
}
if (value === undefined) return;

View file

@ -5,6 +5,8 @@ import {
type INodeExecutionData,
type INodeType,
type INodeTypeDescription,
type ILoadOptionsFunctions,
type IDataObject,
} from 'n8n-workflow';
import set from 'lodash/set';
@ -12,6 +14,7 @@ import set from 'lodash/set';
import { JavaScriptSandbox } from '../Code/JavaScriptSandbox';
import { getSandboxContext } from '../Code/Sandbox';
import { standardizeOutput } from '../Code/utils';
import { generateMessage, type RequestPayload } from './test-setup';
const { CODE_ENABLE_STDOUT } = process.env;
@ -30,6 +33,16 @@ export class AiTransform implements INodeType {
outputs: [NodeConnectionType.Main],
parameterPane: 'wide',
properties: [
{
displayName: 'OpenAI API Key',
name: 'openAiApiKey',
type: 'string',
typeOptions: {
password: true,
},
default: '',
isNodeSetting: true,
},
{
displayName: 'Instructions',
name: 'instructions',
@ -47,6 +60,7 @@ export class AiTransform implements INodeType {
action: {
type: 'askAiCodeGeneration',
target: 'jsCode',
handler: 'generateCode',
},
},
},
@ -79,6 +93,22 @@ export class AiTransform implements INodeType {
],
};
methods = {
actionHandler: {
async generateCode(this: ILoadOptionsFunctions, payload: IDataObject | string | undefined) {
const apiKey = this.getNodeParameter('openAiApiKey', 0) as string;
const code = await generateMessage.call(
this,
[{ role: 'user', content: (payload as unknown as RequestPayload)?.question || '' }],
apiKey,
);
return code;
},
},
};
async execute(this: IExecuteFunctions) {
const workflowMode = this.getMode();

View file

@ -0,0 +1,54 @@
import axios from 'axios';
import { type IDataObject, type ILoadOptionsFunctions, NodeApiError } from 'n8n-workflow';
interface Message {
role: 'user' | 'assistant' | 'system';
content: string;
}
interface ChatResponse {
choices: Array<{ message: Message }>;
}
export interface RequestPayload {
question: string;
context: {
schema: NodeExecutionSchema[];
inputSchema: NodeExecutionSchema;
pushRef: string;
ndvPushRef: string;
};
forNode: 'code' | 'transform';
}
export interface NodeExecutionSchema {
nodeName: string;
schema: IDataObject;
}
export async function generateMessage(
this: ILoadOptionsFunctions,
messages: Message[],
API_KEY: string,
): Promise<string | null> {
try {
const response = await axios.post<ChatResponse>(
'https://api.openai.com/v1/chat/completions',
{
model: 'gpt-4o',
messages,
},
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${API_KEY}`,
},
},
);
const message = response.data.choices[0]?.message.content;
return message || null;
} catch (error) {
throw new NodeApiError(this.getNode(), error);
}
}