Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
yoheinakajima authored Sep 1, 2023
1 parent d10b08c commit 4569caf
Show file tree
Hide file tree
Showing 69 changed files with 3,828 additions and 0 deletions.
83 changes: 83 additions & 0 deletions classic/babyfoxagi/README.md
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.
140 changes: 140 additions & 0 deletions classic/babyfoxagi/babyagi.py
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)
1 change: 1 addition & 0 deletions classic/babyfoxagi/forever_cache.ndjson
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?"}
Loading

0 comments on commit 4569caf

Please sign in to comment.