mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
fix(editor): Adjust Ask AI tracking events & pass NDV session id (no-changelog) (#7027)
Github issue / Community forum post (link here to close automatically): --------- Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
@@ -110,23 +110,17 @@ describe('Code node', () => {
|
|||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
body: {
|
body: {
|
||||||
data: {
|
data: {
|
||||||
code: 'console.log("Hello World")',
|
code: 'console.log("Hello World")'
|
||||||
usage: {
|
|
||||||
prompt_tokens: 15,
|
|
||||||
completion_tokens: 15,
|
|
||||||
total_tokens: 30
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}).as('ask-ai');
|
}).as('ask-ai');
|
||||||
|
|
||||||
cy.getByTestId('ask-ai-cta').click();
|
cy.getByTestId('ask-ai-cta').click();
|
||||||
cy.wait('@ask-ai')
|
const askAiReq = cy.wait('@ask-ai')
|
||||||
.its('request.body')
|
|
||||||
.should('deep.include', {
|
askAiReq.its('request.body').should('have.keys', ['question', 'model', 'context', 'n8nVersion']);
|
||||||
question: prompt,
|
|
||||||
model: "gpt-3.5-turbo-16k",
|
askAiReq.its('context').should('have.keys', ['schema', 'ndvSessionId', 'sessionId']);
|
||||||
context: { schema: [] }
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.contains('Code generation completed').should('be.visible')
|
cy.contains('Code generation completed').should('be.visible')
|
||||||
cy.getByTestId('code-node-tab-code').should('contain.text', 'console.log("Hello World")');
|
cy.getByTestId('code-node-tab-code').should('contain.text', 'console.log("Hello World")');
|
||||||
|
|||||||
@@ -2,12 +2,6 @@ import type { IRestApiContext, Schema } from '@/Interface';
|
|||||||
import { makeRestApiRequest } from '@/utils/apiUtils';
|
import { makeRestApiRequest } from '@/utils/apiUtils';
|
||||||
import type { IDataObject } from 'n8n-workflow';
|
import type { IDataObject } from 'n8n-workflow';
|
||||||
|
|
||||||
type Usage = {
|
|
||||||
prompt_tokens: number;
|
|
||||||
completion_tokens: number;
|
|
||||||
total_tokens: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function generateCodeForPrompt(
|
export async function generateCodeForPrompt(
|
||||||
ctx: IRestApiContext,
|
ctx: IRestApiContext,
|
||||||
{
|
{
|
||||||
@@ -20,11 +14,13 @@ export async function generateCodeForPrompt(
|
|||||||
context: {
|
context: {
|
||||||
schema: Array<{ nodeName: string; schema: Schema }>;
|
schema: Array<{ nodeName: string; schema: Schema }>;
|
||||||
inputSchema: { nodeName: string; schema: Schema };
|
inputSchema: { nodeName: string; schema: Schema };
|
||||||
|
sessionId: string;
|
||||||
|
ndvSessionId: string;
|
||||||
};
|
};
|
||||||
model: string;
|
model: string;
|
||||||
n8nVersion: string;
|
n8nVersion: string;
|
||||||
},
|
},
|
||||||
): Promise<{ code: string; usage: Usage }> {
|
): Promise<{ code: string }> {
|
||||||
return makeRestApiRequest(ctx, 'POST', '/ask-ai', {
|
return makeRestApiRequest(ctx, 'POST', '/ask-ai', {
|
||||||
question,
|
question,
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type { CodeExecutionMode, INodeExecutionData } from 'n8n-workflow';
|
|||||||
import type { BaseTextKey } from '@/plugins/i18n';
|
import type { BaseTextKey } from '@/plugins/i18n';
|
||||||
import type { INodeUi, Schema } from '@/Interface';
|
import type { INodeUi, Schema } from '@/Interface';
|
||||||
import { generateCodeForPrompt } from '@/api/ai';
|
import { generateCodeForPrompt } from '@/api/ai';
|
||||||
import { useDataSchema, useI18n, useMessage, useToast, useTelemetry } from '@/composables';
|
import { useDataSchema, useI18n, useMessage, useTelemetry, useToast } from '@/composables';
|
||||||
import { useNDVStore, usePostHog, useRootStore, useWorkflowsStore } from '@/stores';
|
import { useNDVStore, usePostHog, useRootStore, useWorkflowsStore } from '@/stores';
|
||||||
import { executionDataToJson } from '@/utils';
|
import { executionDataToJson } from '@/utils';
|
||||||
import {
|
import {
|
||||||
@@ -131,10 +131,6 @@ async function onSubmit() {
|
|||||||
if (!activeNode) return;
|
if (!activeNode) return;
|
||||||
const schemas = getSchemas();
|
const schemas = getSchemas();
|
||||||
|
|
||||||
useTelemetry().trackAskAI('ask.generationClicked', {
|
|
||||||
prompt: prompt.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (props.hasChanges) {
|
if (props.hasChanges) {
|
||||||
const confirmModal = await alert(i18n.baseText('codeNodeEditor.askAi.areYouSureToReplace'), {
|
const confirmModal = await alert(i18n.baseText('codeNodeEditor.askAi.areYouSureToReplace'), {
|
||||||
title: i18n.baseText('codeNodeEditor.askAi.replaceCurrentCode'),
|
title: i18n.baseText('codeNodeEditor.askAi.replaceCurrentCode'),
|
||||||
@@ -157,9 +153,14 @@ async function onSubmit() {
|
|||||||
? 'gpt-4'
|
? 'gpt-4'
|
||||||
: 'gpt-3.5-turbo-16k';
|
: 'gpt-3.5-turbo-16k';
|
||||||
|
|
||||||
const { code, usage } = await generateCodeForPrompt(getRestApiContext, {
|
const { code } = await generateCodeForPrompt(getRestApiContext, {
|
||||||
question: prompt.value,
|
question: prompt.value,
|
||||||
context: { schema: schemas.parentNodesSchemas, inputSchema: schemas.inputSchema! },
|
context: {
|
||||||
|
schema: schemas.parentNodesSchemas,
|
||||||
|
inputSchema: schemas.inputSchema!,
|
||||||
|
ndvSessionId: useNDVStore().sessionId,
|
||||||
|
sessionId: useRootStore().sessionId,
|
||||||
|
},
|
||||||
model,
|
model,
|
||||||
n8nVersion: version,
|
n8nVersion: version,
|
||||||
});
|
});
|
||||||
@@ -170,6 +171,10 @@ async function onSubmit() {
|
|||||||
type: 'success',
|
type: 'success',
|
||||||
title: i18n.baseText('codeNodeEditor.askAi.generationCompleted'),
|
title: i18n.baseText('codeNodeEditor.askAi.generationCompleted'),
|
||||||
});
|
});
|
||||||
|
useTelemetry().trackAskAI('askAi.generationFinished', {
|
||||||
|
prompt: prompt.value,
|
||||||
|
code,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showMessage({
|
showMessage({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
@@ -177,6 +182,11 @@ async function onSubmit() {
|
|||||||
message: getErrorMessageByStatusCode(error.httpStatusCode || error?.response.status),
|
message: getErrorMessageByStatusCode(error.httpStatusCode || error?.response.status),
|
||||||
});
|
});
|
||||||
stopLoading();
|
stopLoading();
|
||||||
|
useTelemetry().trackAskAI('askAi.generationFinished', {
|
||||||
|
prompt: prompt.value,
|
||||||
|
code: '',
|
||||||
|
hasError: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function triggerLoadingChange() {
|
function triggerLoadingChange() {
|
||||||
|
|||||||
@@ -99,10 +99,10 @@ export default defineComponent({
|
|||||||
return Boolean(this.nodeType && this.nodeType.name === MANUAL_TRIGGER_NODE_TYPE);
|
return Boolean(this.nodeType && this.nodeType.name === MANUAL_TRIGGER_NODE_TYPE);
|
||||||
},
|
},
|
||||||
isPollingTypeNode(): boolean {
|
isPollingTypeNode(): boolean {
|
||||||
return !!(this.nodeType && this.nodeType.polling);
|
return !!this.nodeType?.polling;
|
||||||
},
|
},
|
||||||
isScheduleTrigger(): boolean {
|
isScheduleTrigger(): boolean {
|
||||||
return !!(this.nodeType && this.nodeType.group.includes('schedule'));
|
return !!this.nodeType?.group.includes('schedule');
|
||||||
},
|
},
|
||||||
isWebhookNode(): boolean {
|
isWebhookNode(): boolean {
|
||||||
return Boolean(this.nodeType && this.nodeType.name === WEBHOOK_NODE_TYPE);
|
return Boolean(this.nodeType && this.nodeType.name === WEBHOOK_NODE_TYPE);
|
||||||
@@ -129,9 +129,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
hasIssues(): boolean {
|
hasIssues(): boolean {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
this.node &&
|
this.node?.issues && (this.node.issues.parameters || this.node.issues.credentials),
|
||||||
this.node.issues &&
|
|
||||||
(this.node.issues.parameters || this.node.issues.credentials),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
disabledHint(): string {
|
disabledHint(): string {
|
||||||
@@ -171,7 +169,7 @@ export default defineComponent({
|
|||||||
return this.$locale.baseText('ndv.execute.listenForTestEvent');
|
return this.$locale.baseText('ndv.execute.listenForTestEvent');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isPollingTypeNode || (this.nodeType && this.nodeType.mockManualExecution)) {
|
if (this.isPollingTypeNode || this.nodeType?.mockManualExecution) {
|
||||||
return this.$locale.baseText('ndv.execute.fetchEvent');
|
return this.$locale.baseText('ndv.execute.fetchEvent');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +219,7 @@ export default defineComponent({
|
|||||||
node_type: this.nodeType ? this.nodeType.name : null,
|
node_type: this.nodeType ? this.nodeType.name : null,
|
||||||
workflow_id: this.workflowsStore.workflowId,
|
workflow_id: this.workflowsStore.workflowId,
|
||||||
source: this.telemetrySource,
|
source: this.telemetrySource,
|
||||||
|
session_id: this.ndvStore.sessionId,
|
||||||
};
|
};
|
||||||
this.$telemetry.track('User clicked execute node button', telemetryPayload);
|
this.$telemetry.track('User clicked execute node button', telemetryPayload);
|
||||||
await this.$externalHooks().run('nodeExecuteButton.onClick', telemetryPayload);
|
await this.$externalHooks().run('nodeExecuteButton.onClick', telemetryPayload);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { useRootStore } from '@/stores/n8nRoot.store';
|
|||||||
import { useTelemetryStore } from '@/stores/telemetry.store';
|
import { useTelemetryStore } from '@/stores/telemetry.store';
|
||||||
import { SLACK_NODE_TYPE } from '@/constants';
|
import { SLACK_NODE_TYPE } from '@/constants';
|
||||||
import { usePostHog } from '@/stores/posthog.store';
|
import { usePostHog } from '@/stores/posthog.store';
|
||||||
|
import { useNDVStore } from '@/stores';
|
||||||
|
|
||||||
export class Telemetry {
|
export class Telemetry {
|
||||||
private pageEventQueue: Array<{ route: RouteLocation }>;
|
private pageEventQueue: Array<{ route: RouteLocation }>;
|
||||||
@@ -134,9 +135,11 @@ export class Telemetry {
|
|||||||
trackAskAI(event: string, properties: IDataObject = {}) {
|
trackAskAI(event: string, properties: IDataObject = {}) {
|
||||||
if (this.rudderStack) {
|
if (this.rudderStack) {
|
||||||
properties.session_id = useRootStore().sessionId;
|
properties.session_id = useRootStore().sessionId;
|
||||||
|
properties.ndv_session_id = useNDVStore().sessionId;
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case 'ask.generationClicked':
|
case 'askAi.generationFinished':
|
||||||
this.track('User clicked on generate code button', properties, { withPostHog: true });
|
this.track('Ai code generation finished', properties, { withPostHog: true });
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -222,7 +225,14 @@ export class Telemetry {
|
|||||||
switch (nodeType) {
|
switch (nodeType) {
|
||||||
case SLACK_NODE_TYPE:
|
case SLACK_NODE_TYPE:
|
||||||
if (change.name === 'parameters.otherOptions.includeLinkToWorkflow') {
|
if (change.name === 'parameters.otherOptions.includeLinkToWorkflow') {
|
||||||
this.track('User toggled n8n reference option');
|
this.track(
|
||||||
|
'User toggled n8n reference option',
|
||||||
|
{
|
||||||
|
node: nodeType,
|
||||||
|
toValue: change.value,
|
||||||
|
},
|
||||||
|
{ withPostHog: true },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type {
|
|||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import type { INodeIssues, IRunData } from 'n8n-workflow';
|
import type { INodeIssues, IRunData } from 'n8n-workflow';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
import { useWorkflowsStore } from './workflows.store';
|
import { useWorkflowsStore } from './workflows.store';
|
||||||
|
|
||||||
export const useNDVStore = defineStore(STORES.NDV, {
|
export const useNDVStore = defineStore(STORES.NDV, {
|
||||||
@@ -163,7 +164,7 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
setNDVSessionId(): void {
|
setNDVSessionId(): void {
|
||||||
this.sessionId = `ndv-${Math.random().toString(36).slice(-8)}`;
|
this.sessionId = `ndv-${uuid()}`;
|
||||||
},
|
},
|
||||||
resetNDVSessionId(): void {
|
resetNDVSessionId(): void {
|
||||||
this.sessionId = '';
|
this.sessionId = '';
|
||||||
|
|||||||
@@ -680,6 +680,7 @@ export default defineComponent({
|
|||||||
node_type: node ? node.type : null,
|
node_type: node ? node.type : null,
|
||||||
workflow_id: this.workflowsStore.workflowId,
|
workflow_id: this.workflowsStore.workflowId,
|
||||||
source: 'canvas',
|
source: 'canvas',
|
||||||
|
session_id: this.ndvStore.sessionId,
|
||||||
};
|
};
|
||||||
this.$telemetry.track('User clicked execute node button', telemetryPayload);
|
this.$telemetry.track('User clicked execute node button', telemetryPayload);
|
||||||
void this.$externalHooks().run('nodeView.onRunNode', telemetryPayload);
|
void this.$externalHooks().run('nodeView.onRunNode', telemetryPayload);
|
||||||
|
|||||||
Reference in New Issue
Block a user