import { Configuration, OpenAIApi } from "openai" import { ChromaClient, OpenAIEmbeddingFunction } from "chromadb" import prompt from "prompt-sync" import assert from "assert" import * as dotenv from "dotenv" dotenv.config() // const client = new ChromaClient("http://localhost:8000") // API Keys const OPENAI_API_KEY = process.env.OPENAI_API_KEY || "" assert(OPENAI_API_KEY, "OPENAI_API_KEY environment variable is missing from .env") const OPENAI_API_MODEL = process.env.OPENAI_API_MODEL || "gpt-3.5-turbo" // Table config const TABLE_NAME = process.env.TABLE_NAME || "" assert(TABLE_NAME, "TABLE_NAME environment variable is missing from .env") // Run config const BABY_NAME = process.env.BABY_NAME || "BabyAGI" // Goal config const p = prompt() const OBJECTIVE = p("What is BabyAGI's objective? ") const INITIAL_TASK = p("What is the initial task to complete the objective? ") assert(OBJECTIVE, "No objective provided.") assert (INITIAL_TASK, "No initial task provided.") console.log('\x1b[95m\x1b[1m\n*****CONFIGURATION*****\n\x1b[0m\x1b[0m') console.log(`Name: ${BABY_NAME}`) console.log(`LLM: ${OPENAI_API_MODEL}`) if (OPENAI_API_MODEL.toLowerCase().includes("gpt-4")){ console.log("\x1b[91m\x1b[1m\n*****USING GPT-4. POTENTIALLY EXPENSIVE. MONITOR YOUR COSTS*****\x1b[0m\x1b[0m") } console.log("\x1b[94m\x1b[1m" + "\n*****OBJECTIVE*****\n" + "\x1b[0m\x1b[0m") console.log(`${OBJECTIVE}`) console.log(`\x1b[93m\x1b[1m \nInitial task: \x1b[0m\x1b[0m ${INITIAL_TASK}`) // Define OpenAI embedding function using Chroma const embeddingFunction = new OpenAIEmbeddingFunction(OPENAI_API_KEY) // Configure OpenAI const configuration = new Configuration({ apiKey: OPENAI_API_KEY, }); const openai = new OpenAIApi(configuration); //Task List var taskList = [] // Connect to chromadb and create/get collection const chromaConnect = async ()=>{ const chroma = new ChromaClient("http://localhost:8000") const metric = "cosine" const collections = await chroma.listCollections() const collectionNames = collections.map((c)=>c.name) if(collectionNames.includes(TABLE_NAME)){ const collection = await chroma.getCollection(TABLE_NAME, embeddingFunction) return collection } else{ const collection = await chroma.createCollection( TABLE_NAME, { "hnsw:space": metric }, embeddingFunction ) return collection } } const add_task = (task)=>{ taskList.push(task) } const clear_tasks = ()=>{ taskList = [] } const get_ada_embedding = async (text)=>{ text = text.replace("\n", " ") const embedding = await embeddingFunction.generate(text) return embedding } const openai_completion = async (prompt, temperature=0.5, maxTokens=100)=>{ if(OPENAI_API_MODEL.startsWith("gpt-")){ const messages = [{"role": "system", "content": prompt}] const response = await openai.createChatCompletion({ model: OPENAI_API_MODEL, messages: messages, max_tokens: maxTokens, temperature: temperature, n: 1, stop: null }) return response.data.choices[0].message.content.trim() } else { const response = await openai.createCompletion({ model: OPENAI_API_MODEL, prompt: prompt, max_tokens: maxTokens, temperature: temperature, top_p: 1, frequency_penalty: 0, presence_penalty: 0 }) return response.data.choices[0].text.trim() } } const task_creation_agent = async (objective, result, task_description, taskList)=>{ const prompt = ` You are an task creation AI that uses the result of an execution agent to create new tasks with the following objective: ${objective}, The last completed task has the result: ${result}. This result was based on this task description: ${task_description}. These are incomplete tasks: ${taskList.map(task=>`${task.taskId}: ${task.taskName}`).join(', ')}. Based on the result, create new tasks to be completed by the AI system that do not overlap with incomplete tasks. Return the tasks as an array.` const response = await openai_completion(prompt) const newTasks = response.trim().includes("\n") ? response.trim().split("\n") : [response.trim()]; return newTasks.map(taskName => ({ taskName: taskName })); } const prioritization_agent = async (taskId)=>{ const taskNames = taskList.map((task)=>task.taskName) const nextTaskId = taskId+1 const prompt = ` You are an task prioritization AI tasked with cleaning the formatting of and reprioritizing the following tasks: ${taskNames}. Consider the ultimate objective of your team:${OBJECTIVE}. Do not remove any tasks. Return the result as a numbered list, like: #. First task #. Second task Start the task list with number ${nextTaskId}.` const response = await openai_completion(prompt) const newTasks = response.trim().includes("\n") ? response.trim().split("\n") : [response.trim()]; clear_tasks() newTasks.forEach((newTask)=>{ const newTaskParts = newTask.trim().split(/\.(?=\s)/) if (newTaskParts.length == 2){ const newTaskId = newTaskParts[0].trim() const newTaskName = newTaskParts[1].trim() add_task({ taskId: newTaskId, taskName: newTaskName }) } }) } const execution_agent = async (objective, task, chromaCollection)=>{ const context = context_agent(objective, 5, chromaCollection) const prompt = ` You are an AI who performs one task based on the following objective: ${objective}.\n Take into account these previously completed tasks: ${context}.\n Your task: ${task}\nResponse:` const response = await openai_completion(prompt, undefined, 2000) return response } const context_agent = async (query, topResultsNum, chromaCollection)=>{ const count = await chromaCollection.count() if (count == 0){ return [] } const results = await chromaCollection.query( undefined, Math.min(topResultsNum, count), undefined, query, ) return results.metadatas[0].map(item=>item.task) } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) } (async()=>{ const initialTask = { taskId: 1, taskName: INITIAL_TASK } add_task(initialTask) const chromaCollection = await chromaConnect() var taskIdCounter = 1 while (true){ if(taskList.length>0){ console.log("\x1b[95m\x1b[1m"+"\n*****TASK LIST*****\n"+"\x1b[0m\x1b[0m") taskList.forEach(t => { console.log(" • " + t.taskName) }) // Step 1: Pull the first task const task = taskList.shift() console.log("\x1b[92m\x1b[1m"+"\n*****NEXT TASK*****\n"+"\x1b[0m\x1b[0m") console.log(task.taskId + ": " + task.taskName) // Send to execution function to complete the task based on the context const result = await execution_agent(OBJECTIVE, task.taskName, chromaCollection) const currTaskId = task.taskId console.log("\x1b[93m\x1b[1m"+"\nTASK RESULT\n"+"\x1b[0m\x1b[0m") console.log(result) // Step 2: Enrich result and store in Chroma const enrichedResult = { data : result} // this is where you should enrich the result if needed const resultId = `result_${task.taskId}` const vector = enrichedResult.data // extract the actual result from the dictionary const collectionLength = (await chromaCollection.get([resultId])).ids?.length if(collectionLength>0){ await chromaCollection.update( resultId, undefined, {task: task.taskName, result: result}, vector ) } else{ await chromaCollection.add( resultId, undefined, {task: task.taskName, result}, vector ) } // Step 3: Create new tasks and reprioritize task list const newTasks = await task_creation_agent(OBJECTIVE, enrichedResult, task.taskName, taskList.map(task=>task.taskName)) newTasks.forEach((task)=>{ taskIdCounter += 1 task.taskId = taskIdCounter add_task(task) }) await prioritization_agent(currTaskId) await sleep(3000) } } })()