mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
Merge branch 'master' of https://github.com/n8n-io/n8n into node-1714-show-result-of-waiting-execution-on-canvas-after-execution
This commit is contained in:
commit
73d383d53d
1
.github/workflows/benchmark-nightly.yml
vendored
1
.github/workflows/benchmark-nightly.yml
vendored
|
@ -84,6 +84,7 @@ jobs:
|
|||
|
||||
# We need to login again because the access token expires
|
||||
- name: Azure login
|
||||
if: always()
|
||||
uses: azure/login@v2.1.1
|
||||
with:
|
||||
client-id: ${{ env.ARM_CLIENT_ID }}
|
||||
|
|
|
@ -374,3 +374,58 @@ describe('AI Assistant Credential Help', () => {
|
|||
aiAssistant.getters.credentialEditAssistantButton().should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
describe('General help', () => {
|
||||
beforeEach(() => {
|
||||
aiAssistant.actions.enableAssistant();
|
||||
wf.actions.visit();
|
||||
});
|
||||
|
||||
it('assistant returns code snippet', () => {
|
||||
cy.intercept('POST', '/rest/ai-assistant/chat', {
|
||||
statusCode: 200,
|
||||
fixture: 'aiAssistant/code_snippet_response.json',
|
||||
}).as('chatRequest');
|
||||
|
||||
aiAssistant.getters.askAssistantFloatingButton().should('be.visible');
|
||||
aiAssistant.getters.askAssistantFloatingButton().click();
|
||||
aiAssistant.getters.askAssistantChat().should('be.visible');
|
||||
aiAssistant.getters.placeholderMessage().should('be.visible');
|
||||
aiAssistant.getters.chatInput().should('be.visible');
|
||||
|
||||
aiAssistant.getters.chatInput().type('Show me an expression');
|
||||
aiAssistant.getters.sendMessageButton().click();
|
||||
|
||||
aiAssistant.getters.chatMessagesAll().should('have.length', 3);
|
||||
aiAssistant.getters.chatMessagesUser().eq(0).should('contain.text', 'Show me an expression');
|
||||
|
||||
aiAssistant.getters
|
||||
.chatMessagesAssistant()
|
||||
.eq(0)
|
||||
.should('contain.text', 'To use expressions in n8n, follow these steps:');
|
||||
|
||||
aiAssistant.getters
|
||||
.chatMessagesAssistant()
|
||||
.eq(0)
|
||||
.should(
|
||||
'include.html',
|
||||
`<pre><code class="language-json">[
|
||||
{
|
||||
"headers": {
|
||||
"host": "n8n.instance.address",
|
||||
...
|
||||
},
|
||||
"params": {},
|
||||
"query": {},
|
||||
"body": {
|
||||
"name": "Jim",
|
||||
"age": 30,
|
||||
"city": "New York"
|
||||
}
|
||||
}
|
||||
]
|
||||
</code></pre>`,
|
||||
);
|
||||
aiAssistant.getters.codeSnippet().should('have.text', '{{$json.body.city}}');
|
||||
});
|
||||
});
|
||||
|
|
28
cypress/fixtures/aiAssistant/code_snippet_response.json
Normal file
28
cypress/fixtures/aiAssistant/code_snippet_response.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"sessionId": "f1d19ed5-0d55-4bad-b49a-f0c56bd6f76f-705b5dbf-12d4-4805-87a3-1e5b3c716d29-W1JgVNrpfitpSNF9rAjB4",
|
||||
"messages": [
|
||||
{
|
||||
"role": "assistant",
|
||||
"type": "message",
|
||||
"text": "To use expressions in n8n, follow these steps:\n\n1. Hover over the parameter where you want to use an expression.\n2. Select **Expressions** in the **Fixed/Expression** toggle.\n3. Write your expression in the parameter, or select **Open expression editor** to open the expressions editor. You can browse the available data in the **Variable selector**. All expressions have the format `{{ your expression here }}`.\n\n### Example: Get data from webhook body\n\nIf your webhook data looks like this:\n\n```json\n[\n {\n \"headers\": {\n \"host\": \"n8n.instance.address\",\n ...\n },\n \"params\": {},\n \"query\": {},\n \"body\": {\n \"name\": \"Jim\",\n \"age\": 30,\n \"city\": \"New York\"\n }\n }\n]\n```\n\nYou can use the following expression to get the value of `city`:\n\n```js\n{{$json.body.city}}\n```\n\nThis expression accesses the incoming JSON-formatted data using n8n's custom `$json` variable and finds the value of `city` (in this example, \"New York\").",
|
||||
"codeSnippet": "{{$json.body.city}}"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"type": "message",
|
||||
"text": "Did this answer solve your question?",
|
||||
"quickReplies": [
|
||||
{
|
||||
"text": "Yes, thanks",
|
||||
"type": "all-good",
|
||||
"isFeedback": true
|
||||
},
|
||||
{
|
||||
"text": "No, I am still stuck",
|
||||
"type": "still-stuck",
|
||||
"isFeedback": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -37,6 +37,7 @@ export class AIAssistant extends BasePage {
|
|||
cy.getByTestId('node-error-view-ask-assistant-button').find('button').first(),
|
||||
credentialEditAssistantButton: () =>
|
||||
cy.getByTestId('credentail-edit-ask-assistant-button').find('button').first(),
|
||||
codeSnippet: () => cy.getByTestId('assistant-code-snippet'),
|
||||
};
|
||||
|
||||
actions = {
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
"semver": "^7.5.4",
|
||||
"tslib": "^2.6.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.5.2",
|
||||
"typescript": "^5.6.2",
|
||||
"ws": ">=8.17.1"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
|
|
|
@ -115,6 +115,7 @@ export class E2EController {
|
|||
) {
|
||||
license.isFeatureEnabled = (feature: BooleanLicenseFeature) =>
|
||||
this.enabledFeatures[feature] ?? false;
|
||||
// @ts-expect-error Overriding method
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
license.getFeatureValue<NumericLicenseFeature> = (feature: NumericLicenseFeature) =>
|
||||
this.numericFeatures[feature] ?? UNLIMITED_LICENSE_QUOTA;
|
||||
|
|
|
@ -14,7 +14,7 @@ import type { SourceControlledFile } from '../types/source-controlled-file';
|
|||
// https://github.com/jestjs/jest/issues/4715
|
||||
function deepSpyOn<O extends object, M extends keyof O>(object: O, methodName: M) {
|
||||
const spy = jest.fn();
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
|
||||
const originalMethod = object[methodName];
|
||||
|
||||
if (typeof originalMethod !== 'function') {
|
||||
|
|
|
@ -21,12 +21,10 @@ function modifyUnderscoredKeys(
|
|||
result[key] = modifier(input[key]);
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
result[key] = input[key];
|
||||
}
|
||||
} else if (typeof input[key] === 'object') {
|
||||
if (Array.isArray(input[key])) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
||||
result[key] = input[key].map((item: any) => {
|
||||
if (typeof item === 'object' && !Array.isArray(item)) {
|
||||
return modifyUnderscoredKeys(item, modifier);
|
||||
|
|
|
@ -950,8 +950,6 @@ export class TelemetryEventRelay extends EventRelay {
|
|||
}: RelayEventMap['user-submitted-personalization-survey']) {
|
||||
const personalizationSurveyData = { user_id: userId } as Record<string, string | string[]>;
|
||||
|
||||
// ESlint is wrong here
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
for (const [camelCaseKey, value] of Object.entries(answers)) {
|
||||
if (value) {
|
||||
personalizationSurveyData[snakeCase(camelCaseKey)] = value;
|
||||
|
|
|
@ -171,6 +171,7 @@ function growInput() {
|
|||
<div
|
||||
v-if="message?.codeSnippet"
|
||||
:class="$style['code-snippet']"
|
||||
data-test-id="assistant-code-snippet"
|
||||
v-html="renderMarkdown(message.codeSnippet).trim()"
|
||||
></div>
|
||||
<BlinkingCursor
|
||||
|
|
|
@ -128,4 +128,24 @@ describe('AskAssistantChat', () => {
|
|||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
it('renders message with code snippet', () => {
|
||||
const { container } = render(AskAssistantChat, {
|
||||
props: {
|
||||
user: { firstName: 'Kobi', lastName: 'Dog' },
|
||||
messages: [
|
||||
{
|
||||
id: '1',
|
||||
type: 'text',
|
||||
role: 'assistant',
|
||||
content:
|
||||
'Hi Max! Here is my top solution to fix the error in your **Transform data** node👇',
|
||||
codeSnippet:
|
||||
"node.on('input', function(msg) {\n if (msg.seed) { dummyjson.seed = msg.seed; }\n try {\n var value = dummyjson.parse(node.template, {mockdata: msg});\n if (node.syntax === 'json') {\n try { value = JSON.parse(value); }\n catch(e) { node.error(RED._('datagen.errors.json-error')); }\n }\n if (node.fieldType === 'msg') {\n RED.util.setMessageProperty(msg,node.field,value);\n }\n else if (node.fieldType === 'flow') {\n node.context().flow.set(node.field,value);\n }\n else if (node.fieldType === 'global') {\n node.context().global.set(node.field,value);\n }\n node.send(msg);\n }\n catch(e) {",
|
||||
read: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1180,6 +1180,248 @@ exports[`AskAssistantChat > renders end of session chat correctly 1`] = `
|
|||
</div>
|
||||
`;
|
||||
|
||||
exports[`AskAssistantChat > renders message with code snippet 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="container"
|
||||
>
|
||||
<div
|
||||
class="header"
|
||||
>
|
||||
<div
|
||||
class="chatTitle"
|
||||
>
|
||||
<div
|
||||
class="headerText"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
width="18"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19.9658 14.0171C19.9679 14.3549 19.8654 14.6851 19.6722 14.9622C19.479 15.2393 19.2046 15.4497 18.8869 15.5645L13.5109 17.5451L11.5303 22.9211C11.4137 23.2376 11.2028 23.5107 10.9261 23.7037C10.6494 23.8966 10.3202 24 9.9829 24C9.64559 24 9.3164 23.8966 9.0397 23.7037C8.76301 23.5107 8.55212 23.2376 8.43549 22.9211L6.45487 17.5451L1.07888 15.5645C0.762384 15.4479 0.489262 15.237 0.296347 14.9603C0.103431 14.6836 0 14.3544 0 14.0171C0 13.6798 0.103431 13.3506 0.296347 13.0739C0.489262 12.7972 0.762384 12.5863 1.07888 12.4697L6.45487 10.4891L8.43549 5.11309C8.55212 4.79659 8.76301 4.52347 9.0397 4.33055C9.3164 4.13764 9.64559 4.0342 9.9829 4.0342C10.3202 4.0342 10.6494 4.13764 10.9261 4.33055C11.2028 4.52347 11.4137 4.79659 11.5303 5.11309L13.5109 10.4891L18.8869 12.4697C19.2046 12.5845 19.479 12.7949 19.6722 13.072C19.8654 13.3491 19.9679 13.6793 19.9658 14.0171ZM14.1056 4.12268H15.7546V5.77175C15.7546 5.99043 15.8415 6.20015 15.9961 6.35478C16.1508 6.50941 16.3605 6.59628 16.5792 6.59628C16.7979 6.59628 17.0076 6.50941 17.1622 6.35478C17.3168 6.20015 17.4037 5.99043 17.4037 5.77175V4.12268H19.0528C19.2715 4.12268 19.4812 4.03581 19.6358 3.88118C19.7905 3.72655 19.8773 3.51682 19.8773 3.29814C19.8773 3.07946 19.7905 2.86974 19.6358 2.71511C19.4812 2.56048 19.2715 2.47361 19.0528 2.47361H17.4037V0.824535C17.4037 0.605855 17.3168 0.396131 17.1622 0.241501C17.0076 0.0868704 16.7979 0 16.5792 0C16.3605 0 16.1508 0.0868704 15.9961 0.241501C15.8415 0.396131 15.7546 0.605855 15.7546 0.824535V2.47361H14.1056C13.8869 2.47361 13.6772 2.56048 13.5225 2.71511C13.3679 2.86974 13.281 3.07946 13.281 3.29814C13.281 3.51682 13.3679 3.72655 13.5225 3.88118C13.6772 4.03581 13.8869 4.12268 14.1056 4.12268ZM23.1755 7.42082H22.3509V6.59628C22.3509 6.3776 22.2641 6.16788 22.1094 6.01325C21.9548 5.85862 21.7451 5.77175 21.5264 5.77175C21.3077 5.77175 21.098 5.85862 20.9434 6.01325C20.7887 6.16788 20.7019 6.3776 20.7019 6.59628V7.42082H19.8773C19.6586 7.42082 19.4489 7.50769 19.2943 7.66232C19.1397 7.81695 19.0528 8.02667 19.0528 8.24535C19.0528 8.46404 19.1397 8.67376 19.2943 8.82839C19.4489 8.98302 19.6586 9.06989 19.8773 9.06989H20.7019V9.89443C20.7019 10.1131 20.7887 10.3228 20.9434 10.4775C21.098 10.6321 21.3077 10.719 21.5264 10.719C21.7451 10.719 21.9548 10.6321 22.1094 10.4775C22.2641 10.3228 22.3509 10.1131 22.3509 9.89443V9.06989H23.1755C23.3941 9.06989 23.6039 8.98302 23.7585 8.82839C23.9131 8.67376 24 8.46404 24 8.24535C24 8.02667 23.9131 7.81695 23.7585 7.66232C23.6039 7.50769 23.3941 7.42082 23.1755 7.42082Z"
|
||||
fill="url(#paint0_linear_173_12825)"
|
||||
/>
|
||||
<defs>
|
||||
<lineargradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="paint0_linear_173_12825"
|
||||
x1="-3.67094e-07"
|
||||
x2="28.8315"
|
||||
y1="-0.000120994"
|
||||
y2="9.82667"
|
||||
>
|
||||
<stop
|
||||
stop-color="var(--color-assistant-highlight-1)"
|
||||
/>
|
||||
<stop
|
||||
offset="0.495"
|
||||
stop-color="var(--color-assistant-highlight-2)"
|
||||
/>
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="var(--color-assistant-highlight-3)"
|
||||
/>
|
||||
</lineargradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<span
|
||||
class="text large"
|
||||
>
|
||||
AI Assistant
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="beta"
|
||||
>
|
||||
beta
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="back"
|
||||
data-test-id="close-chat-button"
|
||||
>
|
||||
<n8n-icon
|
||||
color="text-base"
|
||||
icon="arrow-right"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
>
|
||||
<div
|
||||
class="messages"
|
||||
>
|
||||
|
||||
<div
|
||||
class="message"
|
||||
data-test-id="chat-message-assistant"
|
||||
>
|
||||
<div
|
||||
class="roleName"
|
||||
>
|
||||
<div
|
||||
class="container small"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
height="10"
|
||||
viewBox="0 0 24 24"
|
||||
width="10"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19.9658 14.0171C19.9679 14.3549 19.8654 14.6851 19.6722 14.9622C19.479 15.2393 19.2046 15.4497 18.8869 15.5645L13.5109 17.5451L11.5303 22.9211C11.4137 23.2376 11.2028 23.5107 10.9261 23.7037C10.6494 23.8966 10.3202 24 9.9829 24C9.64559 24 9.3164 23.8966 9.0397 23.7037C8.76301 23.5107 8.55212 23.2376 8.43549 22.9211L6.45487 17.5451L1.07888 15.5645C0.762384 15.4479 0.489262 15.237 0.296347 14.9603C0.103431 14.6836 0 14.3544 0 14.0171C0 13.6798 0.103431 13.3506 0.296347 13.0739C0.489262 12.7972 0.762384 12.5863 1.07888 12.4697L6.45487 10.4891L8.43549 5.11309C8.55212 4.79659 8.76301 4.52347 9.0397 4.33055C9.3164 4.13764 9.64559 4.0342 9.9829 4.0342C10.3202 4.0342 10.6494 4.13764 10.9261 4.33055C11.2028 4.52347 11.4137 4.79659 11.5303 5.11309L13.5109 10.4891L18.8869 12.4697C19.2046 12.5845 19.479 12.7949 19.6722 13.072C19.8654 13.3491 19.9679 13.6793 19.9658 14.0171ZM14.1056 4.12268H15.7546V5.77175C15.7546 5.99043 15.8415 6.20015 15.9961 6.35478C16.1508 6.50941 16.3605 6.59628 16.5792 6.59628C16.7979 6.59628 17.0076 6.50941 17.1622 6.35478C17.3168 6.20015 17.4037 5.99043 17.4037 5.77175V4.12268H19.0528C19.2715 4.12268 19.4812 4.03581 19.6358 3.88118C19.7905 3.72655 19.8773 3.51682 19.8773 3.29814C19.8773 3.07946 19.7905 2.86974 19.6358 2.71511C19.4812 2.56048 19.2715 2.47361 19.0528 2.47361H17.4037V0.824535C17.4037 0.605855 17.3168 0.396131 17.1622 0.241501C17.0076 0.0868704 16.7979 0 16.5792 0C16.3605 0 16.1508 0.0868704 15.9961 0.241501C15.8415 0.396131 15.7546 0.605855 15.7546 0.824535V2.47361H14.1056C13.8869 2.47361 13.6772 2.56048 13.5225 2.71511C13.3679 2.86974 13.281 3.07946 13.281 3.29814C13.281 3.51682 13.3679 3.72655 13.5225 3.88118C13.6772 4.03581 13.8869 4.12268 14.1056 4.12268ZM23.1755 7.42082H22.3509V6.59628C22.3509 6.3776 22.2641 6.16788 22.1094 6.01325C21.9548 5.85862 21.7451 5.77175 21.5264 5.77175C21.3077 5.77175 21.098 5.85862 20.9434 6.01325C20.7887 6.16788 20.7019 6.3776 20.7019 6.59628V7.42082H19.8773C19.6586 7.42082 19.4489 7.50769 19.2943 7.66232C19.1397 7.81695 19.0528 8.02667 19.0528 8.24535C19.0528 8.46404 19.1397 8.67376 19.2943 8.82839C19.4489 8.98302 19.6586 9.06989 19.8773 9.06989H20.7019V9.89443C20.7019 10.1131 20.7887 10.3228 20.9434 10.4775C21.098 10.6321 21.3077 10.719 21.5264 10.719C21.7451 10.719 21.9548 10.6321 22.1094 10.4775C22.2641 10.3228 22.3509 10.1131 22.3509 9.89443V9.06989H23.1755C23.3941 9.06989 23.6039 8.98302 23.7585 8.82839C23.9131 8.67376 24 8.46404 24 8.24535C24 8.02667 23.9131 7.81695 23.7585 7.66232C23.6039 7.50769 23.3941 7.42082 23.1755 7.42082Z"
|
||||
fill="white"
|
||||
/>
|
||||
<defs>
|
||||
<lineargradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="paint0_linear_173_12825"
|
||||
x1="-3.67094e-07"
|
||||
x2="28.8315"
|
||||
y1="-0.000120994"
|
||||
y2="9.82667"
|
||||
>
|
||||
<stop
|
||||
stop-color="var(--color-assistant-highlight-1)"
|
||||
/>
|
||||
<stop
|
||||
offset="0.495"
|
||||
stop-color="var(--color-assistant-highlight-2)"
|
||||
/>
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="var(--color-assistant-highlight-3)"
|
||||
/>
|
||||
</lineargradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<span>
|
||||
Assistant
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="textMessage"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div
|
||||
class="assistantText"
|
||||
>
|
||||
<p>
|
||||
Hi Max! Here is my top solution to fix the error in your
|
||||
<strong>
|
||||
Transform data
|
||||
</strong>
|
||||
node👇
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="code-snippet"
|
||||
data-test-id="assistant-code-snippet"
|
||||
>
|
||||
<p>
|
||||
node.on('input', function(msg) {
|
||||
<br />
|
||||
|
||||
if (msg.seed) { dummyjson.seed = msg.seed; }
|
||||
<br />
|
||||
|
||||
try {
|
||||
<br />
|
||||
|
||||
var value = dummyjson.parse(node.template, {mockdata: msg});
|
||||
<br />
|
||||
|
||||
if (node.syntax === 'json') {
|
||||
<br />
|
||||
|
||||
try { value = JSON.parse(value); }
|
||||
<br />
|
||||
|
||||
catch(e) { node.error(RED._('datagen.errors.json-error')); }
|
||||
<br />
|
||||
|
||||
}
|
||||
<br />
|
||||
|
||||
if (node.fieldType === 'msg') {
|
||||
<br />
|
||||
|
||||
RED.util.setMessageProperty(msg,node.field,value);
|
||||
<br />
|
||||
|
||||
}
|
||||
<br />
|
||||
|
||||
else if (node.fieldType === 'flow') {
|
||||
<br />
|
||||
|
||||
node.context().flow.set(node.field,value);
|
||||
<br />
|
||||
|
||||
}
|
||||
<br />
|
||||
|
||||
else if (node.fieldType === 'global') {
|
||||
<br />
|
||||
|
||||
node.context().global.set(node.field,value);
|
||||
<br />
|
||||
|
||||
}
|
||||
<br />
|
||||
|
||||
node.send(msg);
|
||||
<br />
|
||||
|
||||
}
|
||||
<br />
|
||||
|
||||
catch(e) {
|
||||
</p>
|
||||
</div>
|
||||
<!--v-if-->
|
||||
</div>
|
||||
<!--v-if-->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!--v-if-->
|
||||
</div>
|
||||
<div
|
||||
class="inputWrapper"
|
||||
data-test-id="chat-input-wrapper"
|
||||
>
|
||||
<textarea
|
||||
data-test-id="chat-input"
|
||||
placeholder="Enter your response..."
|
||||
rows="1"
|
||||
wrap="hard"
|
||||
/>
|
||||
<n8n-icon-button
|
||||
class="sendButton"
|
||||
data-test-id="send-message-button"
|
||||
disabled="true"
|
||||
icon="paper-plane"
|
||||
size="large"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AskAssistantChat > renders streaming chat correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
|
|
|
@ -83,6 +83,8 @@ function onClose() {
|
|||
:style="{ width: `${assistantStore.chatWidth}px` }"
|
||||
:class="$style.wrapper"
|
||||
data-test-id="ask-assistant-chat"
|
||||
tabindex="0"
|
||||
@keydown.stop
|
||||
>
|
||||
<AskAssistantChat
|
||||
:user="user"
|
||||
|
|
|
@ -16,12 +16,23 @@ import { DEFAULT_POSTHOG_SETTINGS } from './posthog.test';
|
|||
import { AI_ASSISTANT_EXPERIMENT } from '@/constants';
|
||||
import { reactive } from 'vue';
|
||||
import * as chatAPI from '@/api/assistant';
|
||||
import * as telemetryModule from '@/composables/useTelemetry';
|
||||
import type { Telemetry } from '@/plugins/telemetry';
|
||||
|
||||
let settingsStore: ReturnType<typeof useSettingsStore>;
|
||||
let posthogStore: ReturnType<typeof usePostHog>;
|
||||
|
||||
const apiSpy = vi.spyOn(chatAPI, 'chatWithAssistant');
|
||||
|
||||
const track = vi.fn();
|
||||
const spy = vi.spyOn(telemetryModule, 'useTelemetry');
|
||||
spy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
track,
|
||||
}) as unknown as Telemetry,
|
||||
);
|
||||
|
||||
const setAssistantEnabled = (enabled: boolean) => {
|
||||
settingsStore.setSettings(
|
||||
merge({}, defaultSettings, {
|
||||
|
@ -63,6 +74,7 @@ describe('AI Assistant store', () => {
|
|||
};
|
||||
posthogStore = usePostHog();
|
||||
posthogStore.init();
|
||||
track.mockReset();
|
||||
});
|
||||
|
||||
it('initializes with default values', () => {
|
||||
|
@ -316,4 +328,67 @@ describe('AI Assistant store', () => {
|
|||
await assistantStore.initErrorHelper(context);
|
||||
expect(apiSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call telemetry for opening assistant with error', async () => {
|
||||
const context: ChatRequest.ErrorContext = {
|
||||
error: {
|
||||
description: '',
|
||||
message: 'Hey',
|
||||
name: 'NodeOperationError',
|
||||
},
|
||||
node: {
|
||||
id: '1',
|
||||
type: 'n8n-nodes-base.stopAndError',
|
||||
typeVersion: 1,
|
||||
name: 'Stop and Error',
|
||||
position: [250, 250],
|
||||
parameters: {},
|
||||
},
|
||||
};
|
||||
const mockSessionId = 'test';
|
||||
|
||||
const assistantStore = useAssistantStore();
|
||||
apiSpy.mockImplementation((_ctx, _payload, onMessage) => {
|
||||
onMessage({
|
||||
messages: [],
|
||||
sessionId: mockSessionId,
|
||||
});
|
||||
});
|
||||
|
||||
await assistantStore.initErrorHelper(context);
|
||||
expect(apiSpy).toHaveBeenCalled();
|
||||
expect(assistantStore.currentSessionId).toEqual(mockSessionId);
|
||||
|
||||
assistantStore.trackUserOpenedAssistant({
|
||||
task: 'error',
|
||||
source: 'error',
|
||||
has_existing_session: true,
|
||||
});
|
||||
expect(track).toHaveBeenCalledWith(
|
||||
'Assistant session started',
|
||||
{
|
||||
chat_session_id: 'test',
|
||||
node_type: 'n8n-nodes-base.stopAndError',
|
||||
task: 'error',
|
||||
credential_type: undefined,
|
||||
},
|
||||
{
|
||||
withPostHog: true,
|
||||
},
|
||||
);
|
||||
|
||||
expect(track).toHaveBeenCalledWith('User opened assistant', {
|
||||
chat_session_id: 'test',
|
||||
error: {
|
||||
description: '',
|
||||
message: 'Hey',
|
||||
name: 'NodeOperationError',
|
||||
},
|
||||
has_existing_session: true,
|
||||
node_type: 'n8n-nodes-base.stopAndError',
|
||||
source: 'error',
|
||||
task: 'error',
|
||||
workflow_id: '__EMPTY__',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -580,7 +580,7 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
|
|||
workflow_id: workflowsStore.workflowId,
|
||||
node_type: chatSessionError.value?.node?.type,
|
||||
error: chatSessionError.value?.error,
|
||||
chat_session_id: currentSessionId,
|
||||
chat_session_id: currentSessionId.value,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -2934,6 +2934,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
if (
|
||||
// @ts-expect-error Deprecated file
|
||||
// eslint-disable-next-line no-constant-binary-expression
|
||||
!(this.workflowPermissions.update ?? this.projectPermissions.workflow.update) ??
|
||||
this.isReadOnlyRoute ??
|
||||
|
@ -2973,6 +2974,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
if (
|
||||
// @ts-expect-error Deprecated file
|
||||
// eslint-disable-next-line no-constant-binary-expression
|
||||
!(this.workflowPermissions.update ?? this.projectPermissions.workflow.update) ??
|
||||
this.isReadOnlyRoute ??
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
|
||||
import {
|
||||
NodeOperationError,
|
||||
NodeConnectionType,
|
||||
|
|
|
@ -22,11 +22,11 @@ export class CompareDatasets implements INodeType {
|
|||
version: [1, 2, 2.1, 2.2, 2.3],
|
||||
description: 'Compare two inputs for changes',
|
||||
defaults: { name: 'Compare Datasets' },
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [NodeConnectionType.Main, NodeConnectionType.Main],
|
||||
inputNames: ['Input A', 'Input B'],
|
||||
requiredInputs: 1,
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
|
||||
|
||||
outputs: [
|
||||
NodeConnectionType.Main,
|
||||
NodeConnectionType.Main,
|
||||
|
|
|
@ -23,7 +23,7 @@ export class Cron implements INodeType {
|
|||
name: 'Cron',
|
||||
color: '#29a568',
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
properties: [
|
||||
|
|
|
@ -82,7 +82,7 @@ const versionDescription: INodeTypeDescription = {
|
|||
activationHint:
|
||||
"Once you’ve finished building your workflow, <a data-key='activate'>activate</a> it to have it also listen continuously (you just won’t see those executions here).",
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
|
|
|
@ -86,7 +86,7 @@ const versionDescription: INodeTypeDescription = {
|
|||
activationHint:
|
||||
"Once you’ve finished building your workflow, <a data-key='activate'>activate</a> it to have it also listen continuously (you just won’t see those executions here).",
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
|
|
|
@ -27,7 +27,7 @@ const descriptionV1: INodeTypeDescription = {
|
|||
defaults: {
|
||||
name: 'n8n Form Trigger',
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
webhooks: [
|
||||
|
|
|
@ -38,7 +38,7 @@ const descriptionV2: INodeTypeDescription = {
|
|||
defaults: {
|
||||
name: 'n8n Form Trigger',
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
webhooks: [
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable n8n-nodes-base/node-filename-against-convention */
|
||||
import {
|
||||
type IExecuteFunctions,
|
||||
type IDataObject,
|
||||
|
|
|
@ -22,7 +22,7 @@ export class Interval implements INodeType {
|
|||
name: 'Interval',
|
||||
color: '#00FF00',
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
properties: [
|
||||
|
|
|
@ -20,7 +20,7 @@ export class SplitInBatchesV2 implements INodeType {
|
|||
color: '#007755',
|
||||
},
|
||||
inputs: [NodeConnectionType.Main],
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
|
||||
|
||||
outputs: [NodeConnectionType.Main, NodeConnectionType.Main],
|
||||
outputNames: ['loop', 'done'],
|
||||
properties: [
|
||||
|
|
|
@ -21,7 +21,7 @@ export class SplitInBatchesV3 implements INodeType {
|
|||
color: '#007755',
|
||||
},
|
||||
inputs: [NodeConnectionType.Main],
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
|
||||
|
||||
outputs: [NodeConnectionType.Main, NodeConnectionType.Main],
|
||||
outputNames: ['done', 'loop'],
|
||||
properties: [
|
||||
|
|
|
@ -20,7 +20,7 @@ export class Start implements INodeType {
|
|||
name: 'Start',
|
||||
color: '#00e000',
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
properties: [
|
||||
|
|
|
@ -17,9 +17,9 @@ export class StickyNote implements INodeType {
|
|||
name: 'Sticky Note',
|
||||
color: '#FFD233',
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
|
||||
|
||||
outputs: [],
|
||||
properties: [
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ export class StopAndError implements INodeType {
|
|||
color: '#ff0000',
|
||||
},
|
||||
inputs: [NodeConnectionType.Main],
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
|
||||
|
||||
outputs: [],
|
||||
properties: [
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ export class WebflowTriggerV1 implements INodeType {
|
|||
defaults: {
|
||||
name: 'Webflow Trigger',
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
|
|
|
@ -26,7 +26,7 @@ export class WebflowTriggerV2 implements INodeType {
|
|||
defaults: {
|
||||
name: 'Webflow Trigger',
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
|
|
|
@ -65,7 +65,7 @@ export class Webhook extends Node {
|
|||
activationHint:
|
||||
"Once you've finished building your workflow, run it without having to click this button by using the production webhook URL.",
|
||||
},
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
||||
|
||||
inputs: [],
|
||||
outputs: `={{(${configuredOutputs})($parameter)}}`,
|
||||
credentials: credentialsProperty(this.authPropertyName),
|
||||
|
|
|
@ -43,7 +43,6 @@ export const looseTypeValidationProperty: INodeProperties = {
|
|||
};
|
||||
|
||||
export const appendAttributionOption: INodeProperties = {
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
displayName: 'Append n8n Attribution',
|
||||
name: 'appendAttribution',
|
||||
type: 'boolean',
|
||||
|
|
852
pnpm-lock.yaml
852
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue