mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
refactor(core): Remove Ask AI HTTP request feature (no-changelog) (#9931)
This commit is contained in:
committed by
GitHub
parent
cef177455e
commit
86018aa6e0
@@ -1,225 +0,0 @@
|
||||
import { ApplicationError, jsonParse } from 'n8n-workflow';
|
||||
import { AIService } from '@/services/ai.service';
|
||||
import config from '@/config';
|
||||
import {
|
||||
generateCurlCommandFallbackPromptTemplate,
|
||||
generateCurlCommandPromptTemplate,
|
||||
} from '@/services/ai/prompts/generateCurl';
|
||||
import { PineconeStore } from '@langchain/pinecone';
|
||||
|
||||
jest.mock('@/config', () => {
|
||||
return {
|
||||
getEnv: jest.fn().mockReturnValue('openai'),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('langchain/output_parsers', () => {
|
||||
return {
|
||||
JsonOutputFunctionsParser: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
parse: jest.fn(),
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('@langchain/pinecone', () => {
|
||||
const similaritySearch = jest.fn().mockImplementation(async () => []);
|
||||
|
||||
return {
|
||||
PineconeStore: {
|
||||
similaritySearch,
|
||||
fromExistingIndex: jest.fn().mockImplementation(async () => ({
|
||||
similaritySearch,
|
||||
})),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('@pinecone-database/pinecone', () => ({
|
||||
Pinecone: jest.fn().mockImplementation(() => ({
|
||||
Index: jest.fn().mockImplementation(() => ({})),
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('@/services/ai/providers/openai', () => {
|
||||
const modelInvoke = jest.fn().mockImplementation(() => ({ curl: 'curl -X GET https://n8n.io' }));
|
||||
|
||||
return {
|
||||
AIProviderOpenAI: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
mapResponse: jest.fn((v) => v),
|
||||
invoke: modelInvoke,
|
||||
model: {
|
||||
invoke: modelInvoke,
|
||||
},
|
||||
modelWithOutputParser: () => ({
|
||||
invoke: modelInvoke,
|
||||
}),
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('AIService', () => {
|
||||
describe('constructor', () => {
|
||||
test('should not assign provider with unknown provider type', async () => {
|
||||
jest.mocked(config).getEnv.mockReturnValue('unknown');
|
||||
const aiService = new AIService();
|
||||
|
||||
expect(aiService.provider).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('prompt', () => {
|
||||
test('should throw if prompting with unknown provider type', async () => {
|
||||
jest.mocked(config).getEnv.mockReturnValue('unknown');
|
||||
|
||||
const aiService = new AIService();
|
||||
|
||||
await expect(async () => await aiService.prompt([])).rejects.toThrow(ApplicationError);
|
||||
});
|
||||
|
||||
test('should call provider.invoke', async () => {
|
||||
jest.mocked(config).getEnv.mockReturnValue('openai');
|
||||
|
||||
const service = new AIService();
|
||||
await service.prompt(['message']);
|
||||
|
||||
expect(service.provider.invoke).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateCurl', () => {
|
||||
test('should call generateCurl fallback if pinecone key is not defined', async () => {
|
||||
jest.mocked(config).getEnv.mockImplementation((key: string) => {
|
||||
if (key === 'ai.pinecone.apiKey') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return 'openai';
|
||||
});
|
||||
|
||||
const service = new AIService();
|
||||
const generateCurlGenericSpy = jest.spyOn(service, 'generateCurlGeneric');
|
||||
service.validateCurl = (v) => v;
|
||||
|
||||
const serviceName = 'Service Name';
|
||||
const serviceRequest = 'Please make a request';
|
||||
|
||||
await service.generateCurl(serviceName, serviceRequest);
|
||||
|
||||
expect(generateCurlGenericSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should call generateCurl fallback if no matched service', async () => {
|
||||
jest.mocked(config).getEnv.mockReturnValue('openai');
|
||||
|
||||
const service = new AIService();
|
||||
const generateCurlGenericSpy = jest.spyOn(service, 'generateCurlGeneric');
|
||||
service.validateCurl = (v) => v;
|
||||
|
||||
const serviceName = 'NoMatchedServiceName';
|
||||
const serviceRequest = 'Please make a request';
|
||||
|
||||
await service.generateCurl(serviceName, serviceRequest);
|
||||
|
||||
expect(generateCurlGenericSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should call generateCurl fallback command if no matched vector store documents', async () => {
|
||||
jest.mocked(config).getEnv.mockReturnValue('openai');
|
||||
|
||||
const service = new AIService();
|
||||
const generateCurlGenericSpy = jest.spyOn(service, 'generateCurlGeneric');
|
||||
service.validateCurl = (v) => v;
|
||||
|
||||
const serviceName = 'OpenAI';
|
||||
const serviceRequest = 'Please make a request';
|
||||
|
||||
await service.generateCurl(serviceName, serviceRequest);
|
||||
|
||||
expect(generateCurlGenericSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should call generateCurl command with documents from vectorStore', async () => {
|
||||
const endpoints = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'OpenAI',
|
||||
pageContent: '{ "example": "value" }',
|
||||
},
|
||||
];
|
||||
const serviceName = 'OpenAI';
|
||||
const serviceRequest = 'Please make a request';
|
||||
|
||||
jest.mocked(config).getEnv.mockReturnValue('openai');
|
||||
jest
|
||||
.mocked((PineconeStore as unknown as { similaritySearch: () => {} }).similaritySearch)
|
||||
.mockImplementation(async () => endpoints);
|
||||
|
||||
const service = new AIService();
|
||||
service.validateCurl = (v) => v;
|
||||
|
||||
await service.generateCurl(serviceName, serviceRequest);
|
||||
|
||||
const messages = await generateCurlCommandPromptTemplate.formatMessages({
|
||||
serviceName,
|
||||
serviceRequest,
|
||||
endpoints: JSON.stringify(endpoints.map((document) => jsonParse(document.pageContent))),
|
||||
});
|
||||
|
||||
expect(service.provider.model.invoke).toHaveBeenCalled();
|
||||
expect(service.provider.model.invoke.mock.calls[0][0].messages).toEqual(messages);
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateCurlGeneric', () => {
|
||||
test('should call prompt with serviceName and serviceRequest', async () => {
|
||||
const serviceName = 'Service Name';
|
||||
const serviceRequest = 'Please make a request';
|
||||
|
||||
const service = new AIService();
|
||||
service.validateCurl = (v) => v;
|
||||
|
||||
await service.generateCurlGeneric(serviceName, serviceRequest);
|
||||
|
||||
const messages = await generateCurlCommandFallbackPromptTemplate.formatMessages({
|
||||
serviceName,
|
||||
serviceRequest,
|
||||
});
|
||||
|
||||
expect(service.provider.model.invoke).toHaveBeenCalled();
|
||||
expect(jest.mocked(service.provider.model.invoke).mock.calls[0][0].messages).toEqual(
|
||||
messages,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateCurl', () => {
|
||||
it('should return the result if curl command starts with "curl"', () => {
|
||||
const aiService = new AIService();
|
||||
const result = { curl: 'curl -X GET https://n8n.io' };
|
||||
const validatedResult = aiService.validateCurl(result);
|
||||
expect(validatedResult).toEqual(result);
|
||||
});
|
||||
|
||||
it('should replace boolean and number placeholders in the curl command', () => {
|
||||
const aiService = new AIService();
|
||||
const result = { curl: 'curl -X GET https://n8n.io -d "{ "key": {{value}} }"' };
|
||||
const expected = { curl: 'curl -X GET https://n8n.io -d "{ "key": "{{value}}" }"' };
|
||||
const validatedResult = aiService.validateCurl(result);
|
||||
expect(validatedResult).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should throw an error if curl command does not start with "curl"', () => {
|
||||
const aiService = new AIService();
|
||||
const result = { curl: 'wget -O - https://n8n.io' };
|
||||
expect(() => aiService.validateCurl(result)).toThrow(ApplicationError);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,193 +0,0 @@
|
||||
import type { INodeProperties, INodePropertyCollection, INodePropertyOptions } from 'n8n-workflow';
|
||||
import {
|
||||
summarizeNodeTypeProperties,
|
||||
summarizeOption,
|
||||
summarizeProperty,
|
||||
} from '@/services/ai/utils/summarizeNodeTypeProperties';
|
||||
|
||||
describe('summarizeOption', () => {
|
||||
it('should return summarized option with value', () => {
|
||||
const option: INodePropertyOptions = {
|
||||
name: 'testOption',
|
||||
value: 'testValue',
|
||||
};
|
||||
|
||||
const result = summarizeOption(option);
|
||||
|
||||
expect(result).toEqual({
|
||||
name: 'testOption',
|
||||
value: 'testValue',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return summarized option with values', () => {
|
||||
const option: INodePropertyCollection = {
|
||||
name: 'testOption',
|
||||
displayName: 'testDisplayName',
|
||||
values: [
|
||||
{
|
||||
name: 'testName',
|
||||
default: '',
|
||||
displayName: 'testDisplayName',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = summarizeOption(option);
|
||||
|
||||
expect(result).toEqual({
|
||||
name: 'testOption',
|
||||
values: [
|
||||
{
|
||||
name: 'testDisplayName',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return summarized property', () => {
|
||||
const option: INodeProperties = {
|
||||
name: 'testName',
|
||||
default: '',
|
||||
displayName: 'testDisplayName',
|
||||
type: 'string',
|
||||
};
|
||||
|
||||
const result = summarizeOption(option);
|
||||
|
||||
expect(result).toEqual({
|
||||
name: 'testDisplayName',
|
||||
type: 'string',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('summarizeProperty', () => {
|
||||
it('should return summarized property with displayOptions', () => {
|
||||
const property: INodeProperties = {
|
||||
default: '',
|
||||
name: 'testName',
|
||||
displayName: 'testDisplayName',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
testOption: ['testValue'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = summarizeProperty(property);
|
||||
|
||||
expect(result).toEqual({
|
||||
name: 'testDisplayName',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
testOption: ['testValue'],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return summarized property with options', () => {
|
||||
const property: INodeProperties = {
|
||||
name: 'testName',
|
||||
displayName: 'testDisplayName',
|
||||
default: '',
|
||||
type: 'string',
|
||||
options: [
|
||||
{
|
||||
name: 'testOption',
|
||||
value: 'testValue',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = summarizeProperty(property);
|
||||
|
||||
expect(result).toEqual({
|
||||
name: 'testDisplayName',
|
||||
type: 'string',
|
||||
options: [
|
||||
{
|
||||
name: 'testOption',
|
||||
value: 'testValue',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return summarized property without displayOptions and options', () => {
|
||||
const property: INodeProperties = {
|
||||
name: 'testName',
|
||||
default: '',
|
||||
displayName: 'testDisplayName',
|
||||
type: 'string',
|
||||
};
|
||||
|
||||
const result = summarizeProperty(property);
|
||||
|
||||
expect(result).toEqual({
|
||||
name: 'testDisplayName',
|
||||
type: 'string',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('summarizeNodeTypeProperties', () => {
|
||||
it('should return summarized properties', () => {
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
name: 'testName1',
|
||||
default: '',
|
||||
displayName: 'testDisplayName1',
|
||||
type: 'string',
|
||||
options: [
|
||||
{
|
||||
name: 'testOption1',
|
||||
value: 'testValue1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'testName2',
|
||||
default: '',
|
||||
displayName: 'testDisplayName2',
|
||||
type: 'number',
|
||||
options: [
|
||||
{
|
||||
name: 'testOption2',
|
||||
value: 'testValue2',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = summarizeNodeTypeProperties(properties);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
name: 'testDisplayName1',
|
||||
type: 'string',
|
||||
options: [
|
||||
{
|
||||
name: 'testOption1',
|
||||
value: 'testValue1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'testDisplayName2',
|
||||
type: 'number',
|
||||
options: [
|
||||
{
|
||||
name: 'testOption2',
|
||||
value: 'testValue2',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user