-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d10b08c
commit 4569caf
Showing
69 changed files
with
3,828 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
### Author's Note | ||
|
||
BabyFoxAGI is the 5th mod of BabyAGI. The earlier 4 were [BabyBeeAGI](https://twitter.com/yoheinakajima/status/1652732735344246784?lang=en), [BabyCatAGI](https://twitter.com/yoheinakajima/status/1657448504112091136), [BabyDeerAGI](https://twitter.com/yoheinakajima/status/1666313838868992001), and [BabyElfAGI](https://twitter.com/yoheinakajima/status/1678443482866933760). Following the evolution will be the easiest way to understand BabyFoxAGI. | ||
|
||
### New Features in BabyFoxAGI | ||
|
||
In BabyFoxAGI, the two newest features are: | ||
|
||
1. **Self-Improvement (Also known as [FOXY Method](https://twitter.com/yoheinakajima/status/1685894298536148992))**: This helps it improve its task list building. | ||
2. **[BabyAGI Experimental UI](https://twitter.com/yoheinakajima/status/1693153307454546331)**: In this feature, the chat is separated from task/output. | ||
|
||
Notable in the chat is the ability to either run one skill quickly or generate a task list and chain skills, where the you see BabyAGI (moved to babyagi.py) comes in. main.py is now the back-end to the Python Flask based chat app (public/templates folder). | ||
|
||
### Known Issues and Limitations | ||
|
||
I had issues with parallel tasks within BabyAGI, so removed that for now. I'm also not streaming the task list or in-between work from these task list runs to the UI. For now, you'll have to monitor that in the console. And in general, lots more room for improvement... but wanted to get this out there :) | ||
|
||
|
||
# BabyFoxAGI - Overview | ||
|
||
BabyFoxAGI is an experimental chat-based UI that can use a variety of skills to accomplish tasks, displayed in a separate panel from the Chat UI, allowing for parallel execution of tasks. Tasks can be accomplished quickly using one skill, or by generating a tasklist and chaining multiple tasks/skills together. | ||
|
||
## Skills | ||
|
||
Skills that are included include text_completion, web_search, drawing (uses html canvas), documentation_search, code_reader, skill_saver, airtable_search, and call_babyagi. Please read through each skill to understand them better. | ||
|
||
## Components | ||
|
||
The project consists mainly of two Python scripts (`main.py` and `babyagi.py`) and a client-side JavaScript file (`Chat.js`), along with an HTML layout (`index.html`). | ||
|
||
### main.py | ||
|
||
#### Role | ||
Acts as the entry point for the Flask web application and handles routes, API calls, and ongoing tasks. | ||
|
||
#### Key Features | ||
- Flask routes for handling HTTP requests. | ||
- Integration with OpenAI's API for text summarization and skill execution. | ||
- Management of ongoing tasks and their statuses. | ||
|
||
### Chat.js | ||
|
||
#### Role | ||
Handles the client-side interaction within the web interface, including capturing user input and displaying messages and task statuses. | ||
|
||
#### Key Features | ||
- Dynamic chat interface for user interaction. | ||
- HTTP requests to the Flask backend for task initiation and status checks. | ||
- Presentation layer for task status and results. | ||
|
||
### index.html | ||
|
||
#### Role | ||
Provides the layout for the web interface, including a chat box for user interaction and an objectives box for task display. | ||
|
||
#### Key Features | ||
- HTML layout that accommodates the chat box and objectives box side-by-side. | ||
|
||
### babyagi.py | ||
|
||
#### Role | ||
Acts as the central orchestrator for task execution, coordinating with various skills to accomplish a predefined objective. | ||
|
||
#### Key Features | ||
- Task and skill registries to manage the execution. | ||
- Main execution loop that iteratively performs tasks based on dependencies and objectives. | ||
- Optional feature to reflect on task outputs and potentially generate new tasks. | ||
|
||
## Flow of Execution | ||
|
||
1. The user interacts with the chat interface, sending commands or inquiries. | ||
2. `main.py` receives these requests and uses OpenAI's API to determine the next steps, which could include executing a skill or creating a task list. | ||
3. If tasks are to be executed, `main.py` delegates to `babyagi.py`. | ||
4. `babyagi.py` uses its main execution loop to perform tasks in the required sequence, based on dependencies and the main objective. | ||
5. The output or status of each task is sent back to the client-side via Flask routes, and displayed using `Chat.js`. | ||
|
||
## Notes | ||
|
||
- The system leverages `.env` for API key management. | ||
- `.ndjson` files are used for persistent storage of chat and task statuses. | ||
- There is an optional `REFLECTION` feature in `babyagi.py` that allows the system to reflect on task outputs and potentially generate new tasks. | ||
|
||
This overview provides a comprehensive look into the functionalities and execution flow of the project, offering both high-level insights and low-level details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import os | ||
from dotenv import load_dotenv | ||
import time | ||
from datetime import datetime | ||
from skills.skill_registry import SkillRegistry | ||
from tasks.task_registry import TaskRegistry | ||
from ongoing_tasks import ongoing_tasks | ||
|
||
load_dotenv() # Load environment variables from .env file | ||
|
||
api_keys = { | ||
'openai': os.getenv('OPENAI_API_KEY'), | ||
'serpapi': os.getenv('SERPAPI_API_KEY') | ||
#'airtable': os.getenv('AIRTABLE_API_KEY') | ||
} | ||
|
||
OBJECTIVE = "Research Yohei Nakajima and write a poem about him." | ||
LOAD_SKILLS = ['web_search', 'text_completion', 'code_reader','google_jobs_api_search','image_generation','startup_analysis','play_music','game_generation'] | ||
#add web_search and documentation_search after you add SERPAPI_API_KEY in your secrets. airtable_search once you've added your AIRTABLE_API_KEY, and add base/table/column data to airtable_search.py, etc... | ||
REFLECTION = False #Experimental reflection step between each task run (when running tasklist) | ||
|
||
def run_single_task(task_id, task, skill_registry, task_outputs, OBJECTIVE, task_registry): | ||
"""Execute a single task and update its status""" | ||
task_output = task_registry.execute_task(task_id, task, skill_registry, task_outputs, OBJECTIVE) | ||
|
||
task_outputs[task_id]["output"] = task_output | ||
task_outputs[task_id]["completed"] = True | ||
task_outputs[task_id]["description"] = task.get('description', 'No description available') | ||
task_outputs[task_id]["skill"] = task.get('skill', 'No skill information available') | ||
|
||
if task_output: | ||
task_registry.update_tasks({"id": task_id, "status": "completed", "result": task_output}) | ||
|
||
completed_task = task_registry.get_task(task_id) | ||
print(f"Task #{task_id}: {completed_task.get('task')} [COMPLETED][{completed_task.get('skill')}]") | ||
|
||
if REFLECTION: | ||
new_tasks, insert_after_ids, tasks_to_update = task_registry.reflect_on_output(task_output, skill_descriptions) | ||
for new_task, after_id in zip(new_tasks, insert_after_ids): | ||
task_registry.add_task(new_task, after_id) | ||
|
||
if isinstance(tasks_to_update, dict) and tasks_to_update: | ||
tasks_to_update = [tasks_to_update] | ||
|
||
for task_to_update in tasks_to_update: | ||
task_registry.update_tasks(task_to_update) | ||
|
||
|
||
|
||
|
||
def run_main_loop(OBJECTIVE, LOAD_SKILLS, api_keys, REFLECTION=False): | ||
"""Main execution loop""" | ||
try: | ||
skill_descriptions = ",".join(f"[{skill.name}: {skill.description}]" for skill in global_skill_registry.skills.values()) | ||
task_registry = TaskRegistry() | ||
task_registry.create_tasklist(OBJECTIVE, skill_descriptions) | ||
|
||
skill_names = [skill.name for skill in global_skill_registry.skills.values()] | ||
session_summary = f"OBJECTIVE:{OBJECTIVE}.#SKILLS:{','.join(skill_names)}.#" | ||
|
||
task_outputs = {task["id"]: {"completed": False, "output": None} for task in task_registry.get_tasks()} | ||
|
||
task_output = None # Initialize task_output to None | ||
|
||
while not all(task["completed"] for task in task_outputs.values()): | ||
tasks = task_registry.get_tasks() | ||
task_registry.print_tasklist(tasks) | ||
|
||
for task in tasks: | ||
if task["id"] not in task_outputs: | ||
task_outputs[task["id"]] = {"completed": False, "output": None} | ||
|
||
ready_tasks = [(task["id"], task) for task in tasks if all((dep in task_outputs and task_outputs[dep]["completed"]) for dep in task.get('dependent_task_ids', [])) and not task_outputs[task["id"]]["completed"]] | ||
|
||
for task_id, task in ready_tasks: | ||
run_single_task(task_id, task, global_skill_registry, task_outputs, OBJECTIVE, task_registry) | ||
|
||
time.sleep(0.1) | ||
|
||
# Assuming the last task in tasks has the latest output. Adjust if your use case is different. | ||
last_task_id = tasks[-1]["id"] if tasks else None | ||
task_output = task_outputs[last_task_id]["output"] if last_task_id else None | ||
|
||
task_registry.reflect_on_final(OBJECTIVE, task_registry.get_tasks(), task_output, skill_descriptions) | ||
global_skill_registry.reflect_skills(OBJECTIVE, task_registry.get_tasks(), task_output, skill_descriptions) | ||
|
||
with open(f'output/output_{datetime.now().strftime("%d_%m_%Y_%H_%M_%S")}.txt', 'w') as file: | ||
file.write(session_summary) | ||
print("...file saved.") | ||
print("END") | ||
|
||
return task_output # Return the last task output | ||
|
||
except Exception as e: | ||
return f"An error occurred: {e}" | ||
|
||
|
||
|
||
# Removed repeated logic for initiating skill registry | ||
global_skill_registry = SkillRegistry(api_keys=api_keys, main_loop_function=run_main_loop, skill_names=LOAD_SKILLS) | ||
|
||
|
||
def execute_skill(skill_name, objective, task_id): | ||
"""Execute a single skill""" | ||
skill = global_skill_registry.get_skill(skill_name) | ||
if skill: | ||
try: | ||
result = skill.execute(objective, "", objective) | ||
ongoing_tasks[task_id].update({"status": "completed", "output": result}) | ||
except Exception as e: | ||
ongoing_tasks[task_id].update({"status": "error", "error": str(e)}) | ||
return task_id | ||
return "Skill not found :(" | ||
|
||
def execute_task_list(objective, api_keys, task_id): | ||
"""Execute a list of tasks""" | ||
try: | ||
task_registry = TaskRegistry() | ||
result = run_main_loop(objective, get_skills(), api_keys) | ||
ongoing_tasks[task_id].update({"status": "completed", "output": result}) | ||
return task_registry.get_tasks(), task_id | ||
except Exception as e: | ||
ongoing_tasks[task_id].update({"status": "error", "error": str(e)}) | ||
print(f"Error in execute_task_list: {e}") | ||
return task_id | ||
|
||
|
||
|
||
def get_skills(): | ||
"""Return the global skill registry""" | ||
# Removed repeated logic for initiating skill registry | ||
global global_skill_registry | ||
print("Returning GLOBAL SKILL REGISTRY") | ||
return global_skill_registry | ||
|
||
# Removed repeated logic for initiating skill registry | ||
global_skill_registry = SkillRegistry(api_keys=api_keys, main_loop_function=run_main_loop, skill_names=LOAD_SKILLS) | ||
|
||
if __name__ == "__main__": | ||
run_main_loop(OBJECTIVE, LOAD_SKILLS, api_keys, REFLECTION) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"role": "assistant", "content": "Hey I'm BabyAGI! How can I help you today?"} |
Oops, something went wrong.