Skip to content

Commit

Permalink
Merge pull request #36 from 10clouds/feat/review-refactor-#1
Browse files Browse the repository at this point in the history
feat/review-refactor-#1
  • Loading branch information
maciejt10c authored Oct 9, 2023
2 parents c5d28e5 + b4db1e7 commit 95830b0
Show file tree
Hide file tree
Showing 39 changed files with 615 additions and 2,492 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = {
semi: 'off',
'@typescript-eslint/no-empty-function': 'off',
'prettier/prettier': 'warn',
'no-return-await': 'warn',
},
ignorePatterns: ['out', 'dist', '**/*.d.ts'],
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { mkdirSync, readFileSync, writeFileSync } from 'fs';
import { mkdirSync, readFileSync } from 'fs';
import { writeFile } from 'node:fs/promises';
import path from 'path';
import { initCLISystems } from '../../src/CLI/setupCLISystems';
import { SolutionWithMeta } from '../../src/stepEvolve/FitnessFunction';
Expand Down Expand Up @@ -159,9 +160,7 @@ const EXAMPLE_KNOWLEDGE: Knowledge[] = [
}),
);
}

const initialSolutions = await Promise.all(initialSolutionsPromises);

const finalSolution = await stepEvolve({
initialSolutions,
threshold: THRESHOLD,
Expand All @@ -175,8 +174,8 @@ const EXAMPLE_KNOWLEDGE: Knowledge[] = [
task,
'Initial solution is: ' + solutionWithMeta.solution + ' ' + solutionWithMeta.totalFitness + ' (' + solutionWithMeta.createdWith + ')' + '.',
);
writeFile(path.join(__dirname, 'logs', `${iteration}.json`), JSON.stringify({ iteration, solutionsWithMeta }, null, 2), 'utf8');
}
writeFileSync(path.join(__dirname, 'logs', `${iteration}.json`), JSON.stringify({ iteration, solutionsWithMeta }, null, 2));
},
onProgressMade: async (
oldSolutionsWithMeta: SolutionWithMeta<string>[],
Expand All @@ -185,7 +184,7 @@ const EXAMPLE_KNOWLEDGE: Knowledge[] = [
newSolutions: SolutionWithMeta<string>[],
iteration: number,
) => {
writeFileSync(path.join(__dirname, 'logs', `${iteration}.json`), JSON.stringify({ iteration, accepted, rejected, newSolutions }, null, 2));
writeFile(path.join(__dirname, 'logs', `${iteration}.json`), JSON.stringify({ iteration, accepted, rejected, newSolutions }, null, 2), 'utf8');
//mutateAppendToLog(task, `Solutions ${oldSolutionsWithMeta.map((s) => s.solution).join(', ')}`);

for (const solutionWithMeta of accepted) {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
"dependencies": {
"async-lock": "^1.4.0",
"dotenv": "^16.3.1",
"firebase": "^9.22.2",
"firebase": "^9.9.2",
"firebase-admin": "^11.9.0",
"gpt-tokenizer": "^2.1.1",
"jsonschema": "^1.4.1",
"node-fetch": "^3.3.1",
"openai": "^3.2.1",
"protobufjs": "^6.11.4",
"zod": "^3.21.4",
"zod-to-json-schema": "^3.21.4"
},
Expand Down
8 changes: 4 additions & 4 deletions score/initTestMinionTask.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import path from 'path';
import fs from 'fs';
import { existsSync } from 'fs';
import { readFile } from 'node:fs/promises';
import { MinionTask } from '../src/minionTasks/MinionTask';
import { getEditorManager } from '../src/managers/EditorManager';
import { WorkspaceFilesKnowledge } from '../src/minionTasks/generateDescriptionForWorkspaceFiles';

export interface Selection {
start: { line: number; character: number };
Expand All @@ -14,8 +14,8 @@ export const initMinionTask = async (userQuery: string, filePath: string, select
let readSelectedText = '';
if (fileName) {
const checkPath = path.join(__dirname, 'score', `${fileName}/selectedText.txt`); // Path to the selectedText file
const selectedTextExists = fs.existsSync(checkPath); // Check if selectedText file exists
readSelectedText = selectedTextExists ? fs.readFileSync(checkPath, 'utf8') : ''; // Read the selectedText file if it exists, else "".
const selectedTextExists = existsSync(checkPath); // Check if selectedText file exists
readSelectedText = selectedTextExists ? await readFile(checkPath, 'utf-8') : ''; // Read the selectedText file if it exists, else "".
}

let start = { line: 0, character: 0 };
Expand Down
21 changes: 11 additions & 10 deletions score/runScore.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { mapLimit } from 'async';
import chalk from 'chalk';
import { OptionValues, program } from 'commander';
import fs from 'fs';
import { existsSync, unlinkSync } from 'fs';
import { readFile, writeFile } from 'node:fs/promises';
import * as glob from 'glob';
import { Validator } from 'jsonschema'; // Imported the jsonschema library
import path from 'path';
Expand Down Expand Up @@ -37,7 +38,7 @@ async function runTest({
iterations?: number;
testQueueName?: string;
}): Promise<void> {
const tests: TestDefinition[] = JSON.parse(fs.readFileSync(path.join(__dirname, 'score', `${fileName}/tests.json`), 'utf8'));
const tests: TestDefinition[] = JSON.parse(await readFile(path.join(__dirname, 'score', `${fileName}/tests.json`), 'utf8'));

// Create a validator instance
const validator = new Validator();
Expand All @@ -52,12 +53,12 @@ async function runTest({
}
}
const testPath = path.join(__dirname, 'score', fileName);
const userQuery = fs.readFileSync(path.join(testPath, `userQuery.txt`), 'utf8');
const userQuery = await readFile(path.join(testPath, `userQuery.txt`), 'utf8');
const knowledegePath = path.resolve(__dirname, testPath, 'knowledge.json');
let knowledge: WorkspaceFilesKnowledge[] = [];

if (fs.existsSync(knowledegePath)) {
knowledge = JSON.parse(fs.readFileSync(knowledegePath, 'utf8'));
if (existsSync(knowledegePath)) {
knowledge = JSON.parse(await readFile(knowledegePath, 'utf8'));
}

const statistics = {
Expand All @@ -69,14 +70,14 @@ async function runTest({
console.log(`Running test for '${fileName} (${iterations} iterations)'`);
const directoryPath = path.resolve(__dirname, `score/${fileName}/temp.txt`);
const testInfoPath = path.resolve(__dirname, `score/${fileName}/testInfo.json`);
const originalFileContent = fs.readFileSync(path.join(__dirname, 'score', `${fileName}/original.txt`), 'utf8');
const testInfo = JSON.parse(fs.readFileSync(path.join(__dirname, 'score', `${fileName}/testInfo.json`), 'utf8'));
const originalFileContent = await readFile(path.join(__dirname, 'score', `${fileName}/original.txt`), 'utf8');
const testInfo = JSON.parse(await readFile(path.join(__dirname, 'score', `${fileName}/testInfo.json`), 'utf8'));

for (let i = 0; i < iterations; i++) {
setupCLISystemsForTest();
logToFile(`Iteration ${i + 1} of ${iterations}`);
console.log('ITERATION: ', i, ` of ${fileName}`);
fs.writeFileSync(directoryPath, originalFileContent);
writeFile(directoryPath, originalFileContent, 'utf8');
const minionTaskFilePath = path.join(__dirname, 'score', `${fileName}/temp.txt`);
const { execution } = await initMinionTask(userQuery, minionTaskFilePath, undefined, fileName);
execution.relevantKnowledge = knowledge;
Expand All @@ -97,7 +98,7 @@ async function runTest({
statistics.passed++;
}

fs.unlinkSync(directoryPath);
unlinkSync(directoryPath);
}

const score = ((100 * statistics.passed) / statistics.total).toFixed();
Expand All @@ -111,7 +112,7 @@ async function runTest({
iterations,
},
];
fs.writeFileSync(testInfoPath, JSON.stringify(testInfo));
writeFile(testInfoPath, JSON.stringify(testInfo), 'utf8');

console.log(`'${chalk.green(fileName)}' score: ${score}%`);
logToFile(`'${fileName}' score: ${score}%`);
Expand Down
5 changes: 3 additions & 2 deletions scripts/version.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bash

# Get the new version without 'v'
NEW_VERSION="${1//v}"
CURRENT_VERSION="$(jq -r '.version' package.json)"
jq ".version = \"$NEW_VERSION\"" package.json > package.json.tmp

sed -i "s/$CURRENT_VERSION/$NEW_VERSION/g" package.json
mv package.json.tmp package.json
7 changes: 2 additions & 5 deletions src/CLI/setupCLISystems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,12 @@ export function initCLISystems() {

setAnalyticsManager(analyticsManager);

setLogProvider(undefined);
setOriginalContentProvider(undefined);

const reportChange = (uri: string) => {
// TODO
// TODO: add functionality to report change in logs
};

const reportChangeInTask = (id: string) => {
// TODO
// TODO: add functionality to report change in task
};

setLogProvider({
Expand Down
34 changes: 18 additions & 16 deletions src/gpt/createFullPromptFromSections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,26 @@ export function createFullPromptFromSections({
sections: { [key: string]: string };
sectionMarker?: string;
}) {
//replace all section markers in intro, sections and outro
intro = intro.replace(new RegExp(sectionMarker, 'g'), '');
outro = outro?.replace(new RegExp(sectionMarker, 'g'), '');
sections = Object.fromEntries(Object.entries(sections).map(([name, section]) => [name, section.replace(new RegExp(sectionMarker, 'g'), '')]));
// Remove section markers from intro and outro
const cleanedIntro = intro.replaceAll(sectionMarker, '');
const cleanedOutro = outro?.replaceAll(sectionMarker, '');

return `
${intro}
// Remove section markers from each section
const cleanedSections = Object.fromEntries(Object.entries(sections).map(([name, section]) => [name, section.replaceAll(sectionMarker, '')]));

// Create the full prompt
const sectionPrompts = Object.entries(cleanedSections)
.map(([name, section]) => {
return `
${sectionMarker} ${name} ${sectionMarker}
${section}`.trim();
})
.join('\n\n');

${Object.entries(sections)
.map(([name, section]) => {
return `
${sectionMarker} ${name} ${sectionMarker}
${section}
`.trim();
})
.join('\n\n')}
return `
${cleanedIntro}
${sectionPrompts}
${outro ?? ''}
`.trim();
${cleanedOutro ?? ''}`.trim();
}
5 changes: 5 additions & 0 deletions src/gpt/ensureIRunThisInRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ interface EnsureICanRunThisInRangeParams {
* for the specified AI model.
*/
export function ensureIRunThisInRange({ prompt, minTokens, preferedTokens, mode }: EnsureICanRunThisInRangeParams): number {
/**
* @EXTRA_BUFFER_FOR_ENCODING_OVERHEAD provides a safety margin for token encoding overhead,
* ensuring you have enough tokens for the operation while accounting for potential
* additional tokens needed during encoding.
*/
const EXTRA_BUFFER_FOR_ENCODING_OVERHEAD = 50;

minTokens = Math.ceil(minTokens);
Expand Down
6 changes: 6 additions & 0 deletions src/gpt/extractParsedLines.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ParsedLine } from './types';

/**
* This function processes the chunkBuffer string line by line, checking for lines starting with "data: " or JSON error objects. If a line starts with "data: ", it removes the prefix, trims whitespace, attempts JSON parsing, and adds the result to an array. If parsing fails, it logs an error and throws an exception. For lines not starting with "data: ", it assumes they are JSON error objects and handles them accordingly. The function repeats this process until there are no more newline characters in chunkBuffer, returning the array of ParsedLine objects and the modified chunkBuffer.
* @param chunkBuffer The chunk buffer to extract parsed lines from.
* @returns A tuple containing the array of parsed lines and the modified chunk buffer.
* @throws An error if an error object is encountered in the chunk buffer.
*/
export function extractParsedLines(chunkBuffer: string): [ParsedLine[], string] {
const parsedLines: ParsedLine[] = [];

Expand Down
10 changes: 3 additions & 7 deletions src/gpt/gptExecute.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import fetch from 'node-fetch';
import { z } from 'zod';
import zodToJsonSchema from 'zod-to-json-schema';
import { DEBUG_RESPONSES } from '../const';
import { getAnalyticsManager } from '../managers/AnalyticsManager';
import { getOpenAICacheManager } from '../managers/OpenAICacheManager';
import { isZodString } from '../utils/isZodString';
Expand All @@ -12,6 +11,7 @@ import { FAST_MODE_TOKENS, GPTExecuteRequestData, GPTExecuteRequestMessage, GPTE
import { countTokens } from './countTokens';

let openAIApiKey: string | undefined;
const MAX_REQUEST_ATTEMPTS = 3;

export function setOpenAIApiKey(apiKey: string) {
openAIApiKey = apiKey;
Expand All @@ -31,7 +31,6 @@ function convertResult<OutputTypeSchema extends z.ZodType>(result: string, outpu
}
}
}

export async function gptExecute<OutputTypeSchema extends z.ZodType>({
fullPrompt,
onChunk = async () => {},
Expand Down Expand Up @@ -99,9 +98,6 @@ export async function gptExecute<OutputTypeSchema extends z.ZodType>({
}),
};

if (DEBUG_RESPONSES) {
// console.log('REQUEST DATA:', requestData);
}
const cachedResult = await getOpenAICacheManager().getCachedResult(requestData);

if (cachedResult && typeof cachedResult === 'string') {
Expand All @@ -112,7 +108,7 @@ export async function gptExecute<OutputTypeSchema extends z.ZodType>({
};
}

for (let attempt = 1; attempt <= 3; attempt++) {
for (let attempt = 1; attempt <= MAX_REQUEST_ATTEMPTS; attempt++) {
try {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
Expand Down Expand Up @@ -145,7 +141,7 @@ export async function gptExecute<OutputTypeSchema extends z.ZodType>({
error: String(error),
});

if (attempt === 3) {
if (attempt === MAX_REQUEST_ATTEMPTS) {
throw error;
}
}
Expand Down
12 changes: 8 additions & 4 deletions src/gpt/processOpenAIResponseStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ export async function processOpenAIResponseStream({
let fullContent = '';
let chunkBuffer = '';

return await new Promise<string>((resolve, reject) => {
stream?.on('data', async (value) => {
return new Promise<string>((resolve, reject) => {
if (!stream) {
return reject('No stream');
}

stream.on('data', async (value) => {
try {
if (isCancelled() || controller.signal.aborted) {
stream.removeAllListeners();
Expand Down Expand Up @@ -60,7 +64,7 @@ export async function processOpenAIResponseStream({
}
});

stream?.on('end', () => {
stream.on('end', () => {
if (isCancelled() || controller.signal.aborted) {
stream.removeAllListeners();
reject(CANCELED_STAGE_NAME);
Expand All @@ -69,7 +73,7 @@ export async function processOpenAIResponseStream({
resolve(fullContent);
});

stream?.on('error', (err) => {
stream.on('error', (err) => {
console.error('Error: ', err);
reject(err);
});
Expand Down
1 change: 0 additions & 1 deletion src/managers/AnalyticsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { serializeMinionTask } from '../minionTasks/SerializedMinionTask';

import * as crypto from 'crypto';

//TODO: consider put it to env file before open source
const firebaseConfig = {
apiKey: 'AIzaSyCM95vbb8kEco1Tyq23wd_7ryVgbzQiCqk',
authDomain: 'minions-diagnostics.firebaseapp.com',
Expand Down
5 changes: 0 additions & 5 deletions src/managers/LogProvider.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
export interface LogProvider {
/*
get logURI() {
return `10minions-log:minionTaskId/${this.id}/${('[' + this.shortName + '].md').replace(/ /g, '%20')}`;
}
*/
reportChangeInTask(taskId: string): void;
}

Expand Down
3 changes: 2 additions & 1 deletion src/managers/SimpleOpenAICacheManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as admin from 'firebase-admin';
import { getAnalyticsManager } from './AnalyticsManager';

// It is used to cache the results of the OpenAI API calls and it is imported by project that use this npm package.
export class SimpleOpenAICacheManager {
private firestore: admin.firestore.Firestore | undefined;

Expand Down Expand Up @@ -35,7 +36,7 @@ export class SimpleOpenAICacheManager {
data.push(doc.data().responseData as string);
}
});

// TODO: add explenation why this is done like that
const randomIndex = Math.floor(Math.random() * data.length);
return data[randomIndex];
}
Expand Down
8 changes: 5 additions & 3 deletions src/minionTasks/advancedCodeChangeStrategy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs, { writeFileSync } from 'fs';
import fs from 'fs';
import { writeFile } from 'node:fs/promises';
import path from 'path';
import { SolutionWithMeta } from '../stepEvolve/FitnessFunction';
import { createSolutionWithMetaWithFitness } from '../stepEvolve/createSolutionWithMetaWithFitness';
Expand Down Expand Up @@ -104,9 +105,10 @@ export const advancedCodeChangeStrategy = async (task: MinionTask, test?: boolea
if (!fs.existsSync(logsPath)) {
fs.mkdirSync(logsPath, { recursive: true });
}
writeFileSync(
writeFile(
path.join(__dirname, 'logs', `${iteration}-${dtFormat(new Date(), 'YYYY-MM-DD_HH-mm-ss')}.json`),
JSON.stringify({ iteration, solutionsWithMeta }, null, 2),
'utf8',
);
},
onProgressMade: async (
Expand All @@ -116,7 +118,7 @@ export const advancedCodeChangeStrategy = async (task: MinionTask, test?: boolea
newSolutions: MinionTaskSolutionWithMeta[],
iteration: number,
) => {
writeFileSync(path.join(__dirname, 'logs', `${iteration}.json`), JSON.stringify({ iteration, accepted, rejected, newSolutions }, null, 2));
writeFile(path.join(__dirname, 'logs', `${iteration}.json`), JSON.stringify({ iteration, accepted, rejected, newSolutions }, null, 2), 'utf8');
mutateAppendToLog(task, `Solutions ${oldSolutionsWithMeta.map((s) => s.solution).join(', ')}`);

for (const solutionWithMeta of accepted) {
Expand Down
Loading

0 comments on commit 95830b0

Please sign in to comment.