refactor(core): Remove Ask AI HTTP request feature (no-changelog) (#9931)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2024-07-04 12:09:45 +02:00
committed by GitHub
parent cef177455e
commit 86018aa6e0
41 changed files with 30 additions and 5198 deletions

View File

@@ -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);
});
});
});

View File

@@ -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',
},
],
},
]);
});
});