From 13f61db4cb009382e3cc893857be2b990cc128b6 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Wed, 23 Oct 2024 07:14:22 -0300 Subject: [PATCH] Prepare files upload --- packages/ethernaut-ai/src/index.js | 4 ++ .../src/internal/assistants/Assistant.js | 10 ++- .../src/internal/assistants/Explainer.js | 4 +- .../src/internal/assistants/Interpreter.js | 6 +- .../src/internal/assistants/utils/files.js | 63 +++++++++++++++++++ packages/ethernaut-ai/src/tasks/interpret.js | 6 +- .../basic-project/files/colors.md | 6 ++ .../basic-project/hardhat.config.js | 1 + 8 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 packages/ethernaut-ai/src/internal/assistants/utils/files.js create mode 100644 packages/ethernaut-ai/test/fixture-projects/basic-project/files/colors.md diff --git a/packages/ethernaut-ai/src/index.js b/packages/ethernaut-ai/src/index.js index d14ef450..35bd13ed 100644 --- a/packages/ethernaut-ai/src/index.js +++ b/packages/ethernaut-ai/src/index.js @@ -32,6 +32,10 @@ extendConfig((config, userConfig) => { localConfig.ai?.interpreter?.additionalInstructions.concat() || userConfig.ethernaut?.ai?.interpreter?.additionalInstructions?.concat() || [], + files: + localConfig.ai?.interpreter?.files.concat() || + userConfig.ethernaut?.ai?.interpreter?.files?.concat() || + [], }, explainer: { additionalInstructions: diff --git a/packages/ethernaut-ai/src/internal/assistants/Assistant.js b/packages/ethernaut-ai/src/internal/assistants/Assistant.js index 5c0d0f52..fa45904b 100644 --- a/packages/ethernaut-ai/src/internal/assistants/Assistant.js +++ b/packages/ethernaut-ai/src/internal/assistants/Assistant.js @@ -3,13 +3,12 @@ const storage = require('../storage') const openai = require('../openai') const debug = require('ethernaut-common/src/ui/debug') const output = require('ethernaut-common/src/ui/output') +const { uploadFiles } = require('./utils/files') const EventEmitter = require('events') const EthernautCliError = require('ethernaut-common/src/error/error') class Assistant extends EventEmitter { - constructor(name, config) { - super() - + async initialize(hre, name, config) { if (hre.config.ethernaut.ai.model !== config.model) { config.model = hre.config.ethernaut.ai.model } @@ -185,6 +184,11 @@ class Assistant extends EventEmitter { text, ) } + + async processFiles(files) { + await uploadFiles(openai(), files) + process.exit(0) + } } module.exports = Assistant diff --git a/packages/ethernaut-ai/src/internal/assistants/Explainer.js b/packages/ethernaut-ai/src/internal/assistants/Explainer.js index e8ffddca..9b2e225a 100644 --- a/packages/ethernaut-ai/src/internal/assistants/Explainer.js +++ b/packages/ethernaut-ai/src/internal/assistants/Explainer.js @@ -3,7 +3,7 @@ const Assistant = require('./Assistant') const Thread = require('../threads/Thread') class Explainer extends Assistant { - constructor(hre) { + async initialize(hre) { const config = require('./configs/explainer.json') const docs = buildDocs(hre) @@ -12,7 +12,7 @@ class Explainer extends Assistant { docs.join('\n'), ) - super('explainer', config) + await super.initialize(hre, 'explainer', config) this.injectAdditionalInstructions( hre.config.ethernaut.ai.explainer.additionalInstructions, diff --git a/packages/ethernaut-ai/src/internal/assistants/Interpreter.js b/packages/ethernaut-ai/src/internal/assistants/Interpreter.js index 5c8d5bac..e93ffc7a 100644 --- a/packages/ethernaut-ai/src/internal/assistants/Interpreter.js +++ b/packages/ethernaut-ai/src/internal/assistants/Interpreter.js @@ -4,16 +4,18 @@ const Action = require('../Action') const debug = require('ethernaut-common/src/ui/debug') class Interpreter extends Assistant { - constructor(hre) { + async initialize(hre) { const config = require('./configs/interpreter.json') config.tools = buildToolsSpec(hre) - super('interpreter', config) + await super.initialize(hre, 'interpreter', config) this.injectAdditionalInstructions( hre.config.ethernaut.ai.interpreter.additionalInstructions, ) + await this.processFiles(hre.config.ethernaut.ai.interpreter.files) + this.on('tool_calls_required', this.processToolCalls) this.hre = hre diff --git a/packages/ethernaut-ai/src/internal/assistants/utils/files.js b/packages/ethernaut-ai/src/internal/assistants/utils/files.js new file mode 100644 index 00000000..f9ef8777 --- /dev/null +++ b/packages/ethernaut-ai/src/internal/assistants/utils/files.js @@ -0,0 +1,63 @@ +const fs = require('fs') +const path = require('path') +const debug = require('ethernaut-common/src/ui/debug') + +async function uploadFiles(openai, files) { + // await _deleteAllFiles(openai) + + const uploadedFiles = await listFiles(openai) + debug.log(`Uploaded files: ${JSON.stringify(uploadedFiles, null, 2)}`, 'ai') + + for (const file of files) { + const filename = path.basename(file) + + if (isUploaded(uploadedFiles, filename)) { + debug.log(`File ${file} already uploaded`, 'ai') + continue + } + + await _uploadFile(openai, file) + } +} + +function isUploaded(uploadedFiles, filename) { + debug.log(`Checking if file ${filename} is uploaded`, 'ai') + + const uploaded = uploadedFiles.find((f) => f.filename === filename) + + if (!uploaded) return false + + if (uploaded.status !== 'processed') return false + + return true +} + +async function listFiles(openai) { + const response = await openai.files.list() + return response.data +} + +async function _uploadFile(openai, file) { + debug.log(`Uploading file ${file}`, 'ai') + + const fileStream = fs.createReadStream(file) + const response = await openai.files.create({ + file: fileStream, + purpose: 'assistants', + }) + debug.log(`Uploaded file ${JSON.stringify(response, null, 2)}`, 'ai') + + return response.id +} + +async function _deleteAllFiles(openai) { + const uploadedFiles = await listFiles(openai) + for (const file of uploadedFiles) { + await openai.files.del(file.id) + } +} + +module.exports = { + uploadFiles, + readFiles: listFiles, +} diff --git a/packages/ethernaut-ai/src/tasks/interpret.js b/packages/ethernaut-ai/src/tasks/interpret.js index 92882eae..5b2e31cb 100644 --- a/packages/ethernaut-ai/src/tasks/interpret.js +++ b/packages/ethernaut-ai/src/tasks/interpret.js @@ -58,12 +58,14 @@ require('../scopes/ai') const buildingAssistantLIstener = () => spinner.progress('Building assistant...', 'ai') - _interpreter = new Interpreter(hre) + _interpreter = new Interpreter() + await _interpreter.initialize(hre) _interpreter.on('status_update', statusUpdateListener) _interpreter.on('building_assistant', buildingAssistantLIstener) _interpreter.on('actions_required', processActions) - _explainer = new Explainer(hre) + _explainer = new Explainer() + await _explainer.initialize(hre) _explainer.on('status_update', statusUpdateListener) _explainer.on('building_assistant', buildingAssistantLIstener) diff --git a/packages/ethernaut-ai/test/fixture-projects/basic-project/files/colors.md b/packages/ethernaut-ai/test/fixture-projects/basic-project/files/colors.md new file mode 100644 index 00000000..5eedc16b --- /dev/null +++ b/packages/ethernaut-ai/test/fixture-projects/basic-project/files/colors.md @@ -0,0 +1,6 @@ +# A guide of how to associate colors with smells + +- The color red smells like the sea +- The color blue smells like a strawberry +- The color green smells like lemon +- The color yellow smells like teen spirit diff --git a/packages/ethernaut-ai/test/fixture-projects/basic-project/hardhat.config.js b/packages/ethernaut-ai/test/fixture-projects/basic-project/hardhat.config.js index edf11510..cf1163c5 100644 --- a/packages/ethernaut-ai/test/fixture-projects/basic-project/hardhat.config.js +++ b/packages/ethernaut-ai/test/fixture-projects/basic-project/hardhat.config.js @@ -19,6 +19,7 @@ module.exports = { ai: { interpreter: { additionalInstructions: [''], + files: ['files/colors.md'], }, explainer: { additionalInstructions: [''],