diff --git a/01OS/.env.example b/01OS/.env.example index 0b80c82..d1749bd 100644 --- a/01OS/.env.example +++ b/01OS/.env.example @@ -6,6 +6,7 @@ ALL_LOCAL=False WHISPER_MODEL_NAME="ggml-tiny.en.bin" WHISPER_MODEL_URL="https://huggingface.co/ggerganov/whisper.cpp/resolve/main/" +TEACH_MODE=False # Uncomment to set your OpenAI API key # OPENAI_API_KEY=sk-... diff --git a/01OS/01OS/clients/base_device.py b/01OS/01OS/clients/base_device.py index a8e75d9..7e6945b 100644 --- a/01OS/01OS/clients/base_device.py +++ b/01OS/01OS/clients/base_device.py @@ -329,5 +329,6 @@ class Device: listener.start() def start(self): - asyncio.run(self.start_async()) - p.terminate() \ No newline at end of file + if os.getenv('TEACH_MODE') == "False": + asyncio.run(self.start_async()) + p.terminate() \ No newline at end of file diff --git a/01OS/01OS/server/server.py b/01OS/01OS/server/server.py index e954e96..553ee27 100644 --- a/01OS/01OS/server/server.py +++ b/01OS/01OS/server/server.py @@ -20,7 +20,7 @@ from .i import configure_interpreter from interpreter import interpreter import ngrok from ..utils.accumulator import Accumulator - +from .teach import teach from .utils.logs import setup_logging from .utils.logs import logger setup_logging() @@ -288,27 +288,30 @@ from uvicorn import Config, Server if __name__ == "__main__": async def main(): - # Start listening - asyncio.create_task(listener()) - - # Start watching the kernel if it's your job to do that - if os.getenv('CODE_RUNNER') == "server": - asyncio.create_task(put_kernel_messages_into_queue(from_computer)) - - server_url = os.getenv('SERVER_URL') - if not server_url: - raise ValueError("The environment variable SERVER_URL is not set. Please set it to proceed.") - parsed_url = urllib.parse.urlparse(server_url) - - # Set up Ngrok - ngrok_auth_token = os.getenv('NGROK_AUTHTOKEN') - if ngrok_auth_token is not None: - await setup_ngrok(ngrok_auth_token, parsed_url) - - logger.info("Starting `server.py`...") + if os.getenv('TEACH_MODE') == "True": + teach() + else: + # Start listening + asyncio.create_task(listener()) + + # Start watching the kernel if it's your job to do that + if os.getenv('CODE_RUNNER') == "server": + asyncio.create_task(put_kernel_messages_into_queue(from_computer)) + + server_url = os.getenv('SERVER_URL') + if not server_url: + raise ValueError("The environment variable SERVER_URL is not set. Please set it to proceed.") + parsed_url = urllib.parse.urlparse(server_url) + + # Set up Ngrok + ngrok_auth_token = os.getenv('NGROK_AUTHTOKEN') + if ngrok_auth_token is not None: + await setup_ngrok(ngrok_auth_token, parsed_url) + + logger.info("Starting `server.py`...") - config = Config(app, host=parsed_url.hostname, port=parsed_url.port, lifespan='on') - server = Server(config) - await server.serve() + config = Config(app, host=parsed_url.hostname, port=parsed_url.port, lifespan='on') + server = Server(config) + await server.serve() asyncio.run(main()) \ No newline at end of file diff --git a/01OS/01OS/server/teach.py b/01OS/01OS/server/teach.py new file mode 100644 index 0000000..78f7a88 --- /dev/null +++ b/01OS/01OS/server/teach.py @@ -0,0 +1,75 @@ +from datetime import datetime +from .utils.logs import setup_logging, logger +import tkinter as tk +import tkinter.simpledialog +from interpreter import interpreter +from tkinter import messagebox +from ..utils.accumulator import Accumulator +import time +import os +import textwrap + +setup_logging() +accumulator = Accumulator() +class Skill: + def __init__(self, name: str): + self.skill_name = name + self.steps = [] + self.code = "" + +def to_camel_case(text): + words = text.split() + camel_case_string = words[0].lower() + ''.join(word.title() for word in words[1:]) + return camel_case_string + +def generate_python_code(function_name, code): + code_string = f'def {to_camel_case(function_name)}():\n' + code_string += f' """{function_name}"""\n' + indented_code = textwrap.indent(code, ' ') + code_string += indented_code + '\n' + return code_string + +def generate_python_steps(function_name, steps): + code_string = f'def {to_camel_case(function_name)}():\n' + code_string += f' """{function_name}"""\n' + code_string += f' print({steps})\n' + return code_string + +def teach(): + root = tk.Tk() + root.withdraw() + + skill_name = tkinter.simpledialog.askstring("Skill Name", "Please enter the name for the skill:") + skill = Skill(skill_name) + while True: + step = tkinter.simpledialog.askstring("Next Step", "Enter the next step (or 'end' to finish): ") + logger.info(f"Performing step: {step}") + if step == "end": + break + + chunk_code = "" + interpreter.computer.languages = [l for l in interpreter.computer.languages if l.name.lower() == "python"] + interpreter.force_task_completion = True + for chunk in interpreter.chat(step, stream=True, display=False): + if "format" in chunk and chunk["format"] == "execution": + content = chunk["content"] + language = content["format"] + code = content["content"] + chunk_code += code + interpreter.computer.run(code, language) + time.sleep(0.05) + accumulator.accumulate(chunk) + + isCorrect = messagebox.askyesno("To Proceed?", "Did I do this step right?") + if isCorrect: + skill.steps.append(step) + skill.code += chunk_code + + # Uncomment this incase you want steps instead of code + #python_code = generate_python_steps(skill.skill_name, skill.steps) + + python_code = generate_python_code(skill.skill_name, skill.code) + SKILLS_DIR = os.path.dirname(__file__) + "/skills" + filename = os.path.join(SKILLS_DIR, f"{skill.skill_name.replace(' ', '_')}.py") + with open(filename, "w") as file: + file.write(python_code) diff --git a/01OS/01OS/utils/accumulator.py b/01OS/01OS/utils/accumulator.py index b6353cd..0129cd3 100644 --- a/01OS/01OS/utils/accumulator.py +++ b/01OS/01OS/utils/accumulator.py @@ -26,7 +26,11 @@ class Accumulator: if "content" not in self.message: self.message["content"] = chunk["content"] else: - self.message["content"] += chunk["content"] + if type(chunk["content"]) == dict: + # dict concatenation cannot happen, so we see if chunk is a dict + self.message["content"]["content"] += chunk["content"]["content"] + else: + self.message["content"] += chunk["content"] return None if "end" in chunk: diff --git a/01OS/start.sh b/01OS/start.sh index 0918533..edbb9f7 100755 --- a/01OS/start.sh +++ b/01OS/start.sh @@ -36,6 +36,12 @@ if [[ "$@" == *"--server"* ]]; then export SERVER_START="True" fi +# Check if "--teach" is passed as an argument +if [[ "$@" == *"--teach"* ]]; then + # If "--teach" is passed, set TEACH_MODE to True + export TEACH_MODE="True" +fi + # Check if "--client" is passed as an argument if [[ "$@" == *"--client"* ]]; then # If "--client" is passed, set CLIENT_START to True diff --git a/README.md b/README.md index d859ea7..fd5fdb3 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,10 @@ The `--local` flag will install and run the [whisper.cpp](https://github.com/gge 01 --local --server --expose # Expose a local server ``` +**Teach Mode (experimental)** + +Running `01 --teach` runs 01 in teach mode, where you can add your own skills for Open Interpreter to use, through an easy-to-follow GUI. +
## Setup for development: