ci(backend-review.yml): add linter step to the backend review workflow (#625)

* ci(backend-review.yml): add linter step to the backend review workflow

* chore(backend-review.yml): remove prettier from lint-action configuration

* chore: apply new linting workflow

* chore(lint-staged.config.js): reorder lint-staged tasks for JavaScript and TypeScript files

* chore(eslint): update ignorePatterns in .eslintrc.js
chore(lint-action): remove prettier option in backend-review.yml
chore(package.json): add lint and lint:fix scripts

* chore(lint-staged.config.js): remove prettier --write command for js, jsx, ts, tsx files

* chore(titleConvo.js): remove unnecessary console.log statement
chore(titleConvo.js): add missing comma in options object

* chore: apply linting to all files

* chore(lint-staged.config.js): update lint-staged configuration to include prettier formatting
This commit is contained in:
Danny Avila 2023-07-14 09:36:49 -04:00 committed by GitHub
parent 637bb6bc11
commit e5336039fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
231 changed files with 1688 additions and 1526 deletions

View file

@ -10,7 +10,7 @@ jest.mock('../../../models', () => {
getMessages: jest.fn(),
saveMessage: jest.fn(),
updateMessage: jest.fn(),
saveConvo: jest.fn()
saveConvo: jest.fn(),
};
};
});
@ -52,7 +52,7 @@ describe('BaseClient', () => {
modelOptions: {
model: 'gpt-3.5-turbo',
temperature: 0,
}
},
};
beforeEach(() => {
@ -60,22 +60,14 @@ describe('BaseClient', () => {
});
test('returns the input messages without instructions when addInstructions() is called with empty instructions', () => {
const messages = [
{ content: 'Hello' },
{ content: 'How are you?' },
{ content: 'Goodbye' },
];
const messages = [{ content: 'Hello' }, { content: 'How are you?' }, { content: 'Goodbye' }];
const instructions = '';
const result = TestClient.addInstructions(messages, instructions);
expect(result).toEqual(messages);
});
test('returns the input messages with instructions properly added when addInstructions() is called with non-empty instructions', () => {
const messages = [
{ content: 'Hello' },
{ content: 'How are you?' },
{ content: 'Goodbye' },
];
const messages = [{ content: 'Hello' }, { content: 'How are you?' }, { content: 'Goodbye' }];
const instructions = { content: 'Please respond to the question.' };
const result = TestClient.addInstructions(messages, instructions);
const expected = [
@ -94,20 +86,21 @@ describe('BaseClient', () => {
{ name: 'User', content: 'I have a question.' },
];
const result = TestClient.concatenateMessages(messages);
const expected = `User:\nHello\n\nAssistant:\nHow can I help you?\n\nUser:\nI have a question.\n\n`;
const expected =
'User:\nHello\n\nAssistant:\nHow can I help you?\n\nUser:\nI have a question.\n\n';
expect(result).toBe(expected);
});
test('refines messages correctly in refineMessages()', async () => {
const messagesToRefine = [
{ role: 'user', content: 'Hello', tokenCount: 10 },
{ role: 'assistant', content: 'How can I help you?', tokenCount: 20 }
{ role: 'assistant', content: 'How can I help you?', tokenCount: 20 },
];
const remainingContextTokens = 100;
const expectedRefinedMessage = {
role: 'assistant',
content: 'Refined answer',
tokenCount: 14 // 'Refined answer'.length
tokenCount: 14, // 'Refined answer'.length
};
const result = await TestClient.refineMessages(messagesToRefine, remainingContextTokens);
@ -120,7 +113,7 @@ describe('BaseClient', () => {
TestClient.refineMessages = jest.fn().mockResolvedValue({
role: 'assistant',
content: 'Refined answer',
tokenCount: 30
tokenCount: 30,
});
const messages = [
@ -148,7 +141,7 @@ describe('BaseClient', () => {
TestClient.refineMessages = jest.fn().mockResolvedValue({
role: 'assistant',
content: 'Refined answer',
tokenCount: 4
tokenCount: 4,
});
const messages = [
@ -176,28 +169,28 @@ describe('BaseClient', () => {
});
test('handles context strategy correctly in handleContextStrategy()', async () => {
TestClient.addInstructions = jest.fn().mockReturnValue([
{ content: 'Hello' },
{ content: 'How can I help you?' },
{ content: 'Please provide more details.' },
{ content: 'I can assist you with that.' }
]);
TestClient.addInstructions = jest
.fn()
.mockReturnValue([
{ content: 'Hello' },
{ content: 'How can I help you?' },
{ content: 'Please provide more details.' },
{ content: 'I can assist you with that.' },
]);
TestClient.getMessagesWithinTokenLimit = jest.fn().mockReturnValue({
context: [
{ content: 'How can I help you?' },
{ content: 'Please provide more details.' },
{ content: 'I can assist you with that.' }
{ content: 'I can assist you with that.' },
],
remainingContextTokens: 80,
messagesToRefine: [
{ content: 'Hello' },
],
messagesToRefine: [{ content: 'Hello' }],
refineIndex: 3,
});
TestClient.refineMessages = jest.fn().mockResolvedValue({
role: 'assistant',
content: 'Refined answer',
tokenCount: 30
tokenCount: 30,
});
TestClient.getTokenCountForResponse = jest.fn().mockReturnValue(40);
@ -206,24 +199,24 @@ describe('BaseClient', () => {
{ content: 'Hello' },
{ content: 'How can I help you?' },
{ content: 'Please provide more details.' },
{ content: 'I can assist you with that.' }
{ content: 'I can assist you with that.' },
];
const formattedMessages = [
{ content: 'Hello' },
{ content: 'How can I help you?' },
{ content: 'Please provide more details.' },
{ content: 'I can assist you with that.' }
{ content: 'I can assist you with that.' },
];
const expectedResult = {
payload: [
{
content: 'Refined answer',
role: 'assistant',
tokenCount: 30
tokenCount: 30,
},
{ content: 'How can I help you?' },
{ content: 'Please provide more details.' },
{ content: 'I can assist you with that.' }
{ content: 'I can assist you with that.' },
],
promptTokens: expect.any(Number),
tokenCountMap: {},
@ -246,7 +239,7 @@ describe('BaseClient', () => {
isCreatedByUser: false,
messageId: expect.any(String),
parentMessageId: expect.any(String),
conversationId: expect.any(String)
conversationId: expect.any(String),
});
const response = await TestClient.sendMessage(userMessage);
@ -261,7 +254,7 @@ describe('BaseClient', () => {
conversationId,
parentMessageId,
getIds: jest.fn(),
onStart: jest.fn()
onStart: jest.fn(),
};
const expectedResult = expect.objectContaining({
@ -270,7 +263,7 @@ describe('BaseClient', () => {
isCreatedByUser: false,
messageId: expect.any(String),
parentMessageId: expect.any(String),
conversationId: opts.conversationId
conversationId: opts.conversationId,
});
const response = await TestClient.sendMessage(userMessage, opts);
@ -300,7 +293,10 @@ describe('BaseClient', () => {
test('loadHistory is called with the correct arguments', async () => {
const opts = { conversationId: '123', parentMessageId: '456' };
await TestClient.sendMessage('Hello, world!', opts);
expect(TestClient.loadHistory).toHaveBeenCalledWith(opts.conversationId, opts.parentMessageId);
expect(TestClient.loadHistory).toHaveBeenCalledWith(
opts.conversationId,
opts.parentMessageId,
);
});
test('getIds is called with the correct arguments', async () => {
@ -310,7 +306,7 @@ describe('BaseClient', () => {
expect(getIds).toHaveBeenCalledWith({
userMessage: expect.objectContaining({ text: 'Hello, world!' }),
conversationId: response.conversationId,
responseMessageId: response.messageId
responseMessageId: response.messageId,
});
});
@ -333,10 +329,10 @@ describe('BaseClient', () => {
isCreatedByUser: expect.any(Boolean),
messageId: expect.any(String),
parentMessageId: expect.any(String),
conversationId: expect.any(String)
conversationId: expect.any(String),
}),
saveOptions,
user
user,
);
});
@ -358,14 +354,16 @@ describe('BaseClient', () => {
test('returns an object with the correct shape', async () => {
const response = await TestClient.sendMessage('Hello, world!', {});
expect(response).toEqual(expect.objectContaining({
sender: expect.any(String),
text: expect.any(String),
isCreatedByUser: expect.any(Boolean),
messageId: expect.any(String),
parentMessageId: expect.any(String),
conversationId: expect.any(String)
}));
expect(response).toEqual(
expect.objectContaining({
sender: expect.any(String),
text: expect.any(String),
isCreatedByUser: expect.any(Boolean),
messageId: expect.any(String),
parentMessageId: expect.any(String),
conversationId: expect.any(String),
}),
);
});
});
});

View file

@ -32,9 +32,11 @@ class FakeClient extends BaseClient {
this.modelOptions = {
...modelOptions,
model: modelOptions.model || 'gpt-3.5-turbo',
temperature: typeof modelOptions.temperature === 'undefined' ? 0.8 : modelOptions.temperature,
temperature:
typeof modelOptions.temperature === 'undefined' ? 0.8 : modelOptions.temperature,
top_p: typeof modelOptions.top_p === 'undefined' ? 1 : modelOptions.top_p,
presence_penalty: typeof modelOptions.presence_penalty === 'undefined' ? 1 : modelOptions.presence_penalty,
presence_penalty:
typeof modelOptions.presence_penalty === 'undefined' ? 1 : modelOptions.presence_penalty,
stop: modelOptions.stop,
};
}
@ -66,7 +68,7 @@ const initializeFakeClient = (apiKey, options, fakeMessages) => {
const orderedMessages = TestClient.constructor.getMessagesForConversation(
fakeMessages,
parentMessageId
parentMessageId,
);
TestClient.currentMessages = orderedMessages;
@ -98,7 +100,7 @@ const initializeFakeClient = (apiKey, options, fakeMessages) => {
this.pastMessages = await TestClient.loadHistory(
conversationId,
TestClient.options?.parentMessageId
TestClient.options?.parentMessageId,
);
const userMessage = {
@ -107,7 +109,7 @@ const initializeFakeClient = (apiKey, options, fakeMessages) => {
isCreatedByUser: true,
messageId: userMessageId,
parentMessageId,
conversationId
conversationId,
};
const response = {
@ -116,7 +118,7 @@ const initializeFakeClient = (apiKey, options, fakeMessages) => {
isCreatedByUser: false,
messageId: crypto.randomUUID(),
parentMessageId: userMessage.messageId,
conversationId
conversationId,
};
fakeMessages.push(userMessage);
@ -126,7 +128,7 @@ const initializeFakeClient = (apiKey, options, fakeMessages) => {
opts.getIds({
userMessage,
conversationId,
responseMessageId: response.messageId
responseMessageId: response.messageId,
});
}
@ -146,7 +148,10 @@ const initializeFakeClient = (apiKey, options, fakeMessages) => {
// userMessage is always the last one in the payload
if (i === payload.length - 1) {
userMessage.tokenCount = message.tokenCount;
console.debug(`Token count for user message: ${tokenCount}`, `Instruction Tokens: ${tokenCountMap.instructions || 'N/A'}`);
console.debug(
`Token count for user message: ${tokenCount}`,
`Instruction Tokens: ${tokenCountMap.instructions || 'N/A'}`,
);
}
return messageWithoutTokenCount;
});
@ -163,7 +168,10 @@ const initializeFakeClient = (apiKey, options, fakeMessages) => {
});
TestClient.buildMessages = jest.fn(async (messages, parentMessageId) => {
const orderedMessages = TestClient.constructor.getMessagesForConversation(messages, parentMessageId);
const orderedMessages = TestClient.constructor.getMessagesForConversation(
messages,
parentMessageId,
);
const formattedMessages = orderedMessages.map((message) => {
let { role: _role, sender, text } = message;
const role = _role ?? sender;
@ -180,6 +188,6 @@ const initializeFakeClient = (apiKey, options, fakeMessages) => {
});
return TestClient;
}
};
module.exports = { FakeClient, initializeFakeClient };
module.exports = { FakeClient, initializeFakeClient };

View file

@ -5,7 +5,7 @@ describe('OpenAIClient', () => {
const model = 'gpt-4';
const parentMessageId = '1';
const messages = [
{ role: 'user', sender: 'User', text: 'Hello', messageId: parentMessageId},
{ role: 'user', sender: 'User', text: 'Hello', messageId: parentMessageId },
{ role: 'assistant', sender: 'Assistant', text: 'Hi', messageId: '2' },
];
@ -22,7 +22,7 @@ describe('OpenAIClient', () => {
client.refineMessages = jest.fn().mockResolvedValue({
role: 'assistant',
content: 'Refined answer',
tokenCount: 30
tokenCount: 30,
});
});
@ -100,60 +100,83 @@ describe('OpenAIClient', () => {
describe('buildMessages', () => {
it('should build messages correctly for chat completion', async () => {
const result = await client.buildMessages(messages, parentMessageId, { isChatCompletion: true });
const result = await client.buildMessages(messages, parentMessageId, {
isChatCompletion: true,
});
expect(result).toHaveProperty('prompt');
});
it('should build messages correctly for non-chat completion', async () => {
const result = await client.buildMessages(messages, parentMessageId, { isChatCompletion: false });
const result = await client.buildMessages(messages, parentMessageId, {
isChatCompletion: false,
});
expect(result).toHaveProperty('prompt');
});
it('should build messages correctly with a promptPrefix', async () => {
const result = await client.buildMessages(messages, parentMessageId, { isChatCompletion: true, promptPrefix: 'Test Prefix' });
const result = await client.buildMessages(messages, parentMessageId, {
isChatCompletion: true,
promptPrefix: 'Test Prefix',
});
expect(result).toHaveProperty('prompt');
const instructions = result.prompt.find(item => item.name === 'instructions');
const instructions = result.prompt.find((item) => item.name === 'instructions');
expect(instructions).toBeDefined();
expect(instructions.content).toContain('Test Prefix');
});
it('should handle context strategy correctly', async () => {
client.contextStrategy = 'refine';
const result = await client.buildMessages(messages, parentMessageId, { isChatCompletion: true });
const result = await client.buildMessages(messages, parentMessageId, {
isChatCompletion: true,
});
expect(result).toHaveProperty('prompt');
expect(result).toHaveProperty('tokenCountMap');
});
it('should assign name property for user messages when options.name is set', async () => {
client.options.name = 'Test User';
const result = await client.buildMessages(messages, parentMessageId, { isChatCompletion: true });
const hasUserWithName = result.prompt.some(item => item.role === 'user' && item.name === 'Test User');
const result = await client.buildMessages(messages, parentMessageId, {
isChatCompletion: true,
});
const hasUserWithName = result.prompt.some(
(item) => item.role === 'user' && item.name === 'Test User',
);
expect(hasUserWithName).toBe(true);
});
it('should calculate tokenCount for each message when contextStrategy is set', async () => {
client.contextStrategy = 'refine';
const result = await client.buildMessages(messages, parentMessageId, { isChatCompletion: true });
const hasUserWithTokenCount = result.prompt.some(item => item.role === 'user' && item.tokenCount > 0);
const result = await client.buildMessages(messages, parentMessageId, {
isChatCompletion: true,
});
const hasUserWithTokenCount = result.prompt.some(
(item) => item.role === 'user' && item.tokenCount > 0,
);
expect(hasUserWithTokenCount).toBe(true);
});
it('should handle promptPrefix from options when promptPrefix argument is not provided', async () => {
client.options.promptPrefix = 'Test Prefix from options';
const result = await client.buildMessages(messages, parentMessageId, { isChatCompletion: true });
const instructions = result.prompt.find(item => item.name === 'instructions');
const result = await client.buildMessages(messages, parentMessageId, {
isChatCompletion: true,
});
const instructions = result.prompt.find((item) => item.name === 'instructions');
expect(instructions.content).toContain('Test Prefix from options');
});
it('should handle case when neither promptPrefix argument nor options.promptPrefix is set', async () => {
const result = await client.buildMessages(messages, parentMessageId, { isChatCompletion: true });
const instructions = result.prompt.find(item => item.name === 'instructions');
const result = await client.buildMessages(messages, parentMessageId, {
isChatCompletion: true,
});
const instructions = result.prompt.find((item) => item.name === 'instructions');
expect(instructions).toBeUndefined();
});
it('should handle case when getMessagesForConversation returns null or an empty array', async () => {
const messages = [];
const result = await client.buildMessages(messages, parentMessageId, { isChatCompletion: true });
const result = await client.buildMessages(messages, parentMessageId, {
isChatCompletion: true,
});
expect(result.prompt).toEqual([]);
});
});

View file

@ -16,7 +16,7 @@ require('dotenv').config();
const { OpenAIClient } = require('../');
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
return new Promise((resolve) => setTimeout(resolve, ms));
}
const run = async () => {
@ -46,7 +46,7 @@ const run = async () => {
model,
},
proxy: process.env.PROXY || null,
debug: true
debug: true,
};
let apiKey = process.env.OPENAI_API_KEY;
@ -59,7 +59,13 @@ const run = async () => {
function printProgressBar(percentageUsed) {
const filledBlocks = Math.round(percentageUsed / 2); // Each block represents 2%
const emptyBlocks = 50 - filledBlocks; // Total blocks is 50 (each represents 2%), so the rest are empty
const progressBar = '[' + '█'.repeat(filledBlocks) + ' '.repeat(emptyBlocks) + '] ' + percentageUsed.toFixed(2) + '%';
const progressBar =
'[' +
'█'.repeat(filledBlocks) +
' '.repeat(emptyBlocks) +
'] ' +
percentageUsed.toFixed(2) +
'%';
console.log(progressBar);
}
@ -78,10 +84,10 @@ const run = async () => {
// encoder.free();
const memoryUsageDuringLoop = process.memoryUsage().heapUsed;
const percentageUsed = memoryUsageDuringLoop / maxMemory * 100;
const percentageUsed = (memoryUsageDuringLoop / maxMemory) * 100;
printProgressBar(percentageUsed);
if (i === (iterations - 1)) {
if (i === iterations - 1) {
console.log(' done');
// encoder.free();
}
@ -100,7 +106,7 @@ const run = async () => {
await timeout(15000);
const memoryUsageAfterTimeout = process.memoryUsage().heapUsed;
console.log(`Post timeout: ${memoryUsageAfterTimeout / 1024 / 1024} megabytes`);
}
};
run();

View file

@ -7,7 +7,7 @@ jest.mock('../../../models/Conversation', () => {
return function () {
return {
save: jest.fn(),
deleteConvos: jest.fn()
deleteConvos: jest.fn(),
};
};
});
@ -19,11 +19,11 @@ describe('PluginsClient', () => {
modelOptions: {
model: 'gpt-3.5-turbo',
temperature: 0,
max_tokens: 2
max_tokens: 2,
},
agentOptions: {
model: 'gpt-3.5-turbo'
}
model: 'gpt-3.5-turbo',
},
};
let parentMessageId;
let conversationId;
@ -43,13 +43,13 @@ describe('PluginsClient', () => {
const orderedMessages = TestAgent.constructor.getMessagesForConversation(
fakeMessages,
parentMessageId
parentMessageId,
);
const chatMessages = orderedMessages.map((msg) =>
msg?.isCreatedByUser || msg?.role?.toLowerCase() === 'user'
? new HumanChatMessage(msg.text)
: new AIChatMessage(msg.text)
: new AIChatMessage(msg.text),
);
TestAgent.currentMessages = orderedMessages;
@ -64,7 +64,7 @@ describe('PluginsClient', () => {
const userMessageId = opts.overrideParentMessageId || crypto.randomUUID();
this.pastMessages = await TestAgent.loadHistory(
conversationId,
TestAgent.options?.parentMessageId
TestAgent.options?.parentMessageId,
);
const userMessage = {
@ -73,7 +73,7 @@ describe('PluginsClient', () => {
isCreatedByUser: true,
messageId: userMessageId,
parentMessageId,
conversationId
conversationId,
};
const response = {
@ -82,7 +82,7 @@ describe('PluginsClient', () => {
isCreatedByUser: false,
messageId: crypto.randomUUID(),
parentMessageId: userMessage.messageId,
conversationId
conversationId,
};
fakeMessages.push(userMessage);
@ -107,7 +107,7 @@ describe('PluginsClient', () => {
isCreatedByUser: false,
messageId: expect.any(String),
parentMessageId: expect.any(String),
conversationId: expect.any(String)
conversationId: expect.any(String),
});
const response = await TestAgent.sendMessage(userMessage);
@ -121,7 +121,7 @@ describe('PluginsClient', () => {
const userMessage = 'Second message in the conversation';
const opts = {
conversationId,
parentMessageId
parentMessageId,
};
const expectedResult = expect.objectContaining({
@ -130,7 +130,7 @@ describe('PluginsClient', () => {
isCreatedByUser: false,
messageId: expect.any(String),
parentMessageId: expect.any(String),
conversationId: opts.conversationId
conversationId: opts.conversationId,
});
const response = await TestAgent.sendMessage(userMessage, opts);