From 86975c40267f00fd0d6cfda086b0ddc3f08bdf4d Mon Sep 17 00:00:00 2001
From: Shiven Mian <shivenmian97@gmail.com>
Date: Tue, 13 Feb 2024 19:44:38 -0800
Subject: [PATCH 1/9] fix: added tom's fixes

---
 .gitignore                       |  1 +
 01OS/01OS/clients/base_device.py | 15 ++++++++++++---
 01OS/01OS/clients/start.sh       |  4 ++--
 01OS/start.sh                    |  7 +++++++
 4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/.gitignore b/.gitignore
index ff21f10..44773f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,3 +167,4 @@ cython_debug/
 
 # ignore the aifs index files
 _.aifs
+01OS/output_audio.wav
\ No newline at end of file
diff --git a/01OS/01OS/clients/base_device.py b/01OS/01OS/clients/base_device.py
index f2197a4..1855253 100644
--- a/01OS/01OS/clients/base_device.py
+++ b/01OS/01OS/clients/base_device.py
@@ -50,7 +50,7 @@ send_queue = queue.Queue()
 
 class Device:
     def __init__(self):
-        pass
+        self.pressed_keys = set()
 
     def record_audio(self):
         
@@ -125,12 +125,21 @@ class Device:
             RECORDING = False
 
     def on_press(self, key):
-        """Detect spacebar press."""
-        if key == keyboard.Key.space:
+        """Detect spacebar press, ESC key press, and Ctrl+C combination."""
+        self.pressed_keys.add(key)  # Add the pressed key to the set
+
+        if keyboard.Key.esc in self.pressed_keys:
+            logger.info("Exiting...")
+            os._exit(0)
+        elif keyboard.Key.space in self.pressed_keys:
             self.toggle_recording(True)
+        elif {keyboard.Key.ctrl, keyboard.KeyCode.from_char('c')} <= self.pressed_keys:
+            logger.info("Ctrl+C pressed. Exiting...")
+            os._exit(0)
 
     def on_release(self, key):
         """Detect spacebar release and ESC key press."""
+        self.pressed_keys.discard(key)  # Remove the released key from the key press tracking set
         if key == keyboard.Key.space:
             self.toggle_recording(False)
         elif key == keyboard.Key.esc or (key == keyboard.Key.ctrl and keyboard.Key.c):
diff --git a/01OS/01OS/clients/start.sh b/01OS/01OS/clients/start.sh
index 8e8edc9..844e6a0 100644
--- a/01OS/01OS/clients/start.sh
+++ b/01OS/01OS/clients/start.sh
@@ -1,8 +1,8 @@
 DEVICE=$(uname -n)
 if [[ "$DEVICE" == "rpi" ]]; then
     cd 01OS
-    python -m 01OS.clients.rpi.device &
+    python -m 01OS.clients.rpi.device
 else
     cd 01OS
-    python -m 01OS.clients.macos.device &
+    python -m 01OS.clients.macos.device
 fi
diff --git a/01OS/start.sh b/01OS/start.sh
index 201f475..36b7ec6 100755
--- a/01OS/start.sh
+++ b/01OS/start.sh
@@ -1,5 +1,12 @@
 #!/usr/bin/env bash
 
+# Set python to prioritize the module files from the current directory
+# If we don't do this, then the python interpreter will not be able to find the modules,
+# and will throw an error like "ModuleNotFoundError: No module named '01OS'".
+# If we solve the problem by pip installing the official 01OS package, then those
+# modules will run instead of the local ones that we are trying to develop with.
+export PYTHONPATH="$(pwd):$PYTHONPATH"
+
 ### Import Environment Variables from .env
 SCRIPT_DIR="$(dirname "$0")"
 if [ ! -f "$SCRIPT_DIR/.env" ]; then

From 818597caa95dc38b524a249b2f9048a8e5f3c032 Mon Sep 17 00:00:00 2001
From: Shiven Mian <shivenmian97@gmail.com>
Date: Tue, 13 Feb 2024 20:09:08 -0800
Subject: [PATCH 2/9] fix: don't output if websocket disconnected normally

---
 01OS/01OS/server/server.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/01OS/01OS/server/server.py b/01OS/01OS/server/server.py
index 62ab6d8..3f38ab5 100644
--- a/01OS/01OS/server/server.py
+++ b/01OS/01OS/server/server.py
@@ -9,7 +9,7 @@ import traceback
 import re
 from fastapi import FastAPI
 from fastapi.responses import PlainTextResponse
-from starlette.websockets import WebSocket
+from starlette.websockets import WebSocket, WebSocketDisconnect
 from .stt.stt import stt_bytes
 from .tts.tts import tts
 from pathlib import Path
@@ -99,6 +99,8 @@ async def websocket_endpoint(websocket: WebSocket):
     send_task = asyncio.create_task(send_messages(websocket))
     try:
         await asyncio.gather(receive_task, send_task)
+    except WebSocketDisconnect:
+        pass
     except Exception as e:
         traceback.print_exc()
         logger.info(f"Connection lost. Error: {e}")

From b76bbc3e40b2215801789dff173977446c906de8 Mon Sep 17 00:00:00 2001
From: Shiven Mian <shivenmian97@gmail.com>
Date: Tue, 13 Feb 2024 20:11:21 -0800
Subject: [PATCH 3/9] fix: remove output_audio

---
 01OS/output_audio.wav | Bin 49382 -> 0 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 01OS/output_audio.wav

diff --git a/01OS/output_audio.wav b/01OS/output_audio.wav
deleted file mode 100644
index 44ec5e663502f7790104b017d96ad3483076cd0e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 49382
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%0D}=ToL}
zjL}U0n6=1&)o|(+s}18gvx3fgJGFwfiM5n{od`-m(}^jwq5oQ3h9E<qeOUj**yQAs
zQ(jIh=j<0)-^1cC{ozWmccx~NgX1moXA*Ok!pd`&>C`)BQJryE+3f7=k=cEguhRyz
zvKeRDKfy8E#{$y|XJ9(O`VOZ>b#j>T%*i!&msvN)eWw<%`0n^RdmN7v9x~nj+BU`^
z_FufLHLPc0J&?1W%$hMyIgjkW=bU{IyUVQQ*QX3smd2@ztYsSP3bUNf-h`FFP-S+8
zmBX%_{AVrwst4?;Gakd3A;EHB)|l0ZX#_huQDZ2xQk+L8`x(+-YtE2nDOio1wfKrJ
zE5q4iv7@te_D_3GiL!A3s~^MP8BbqQ4_0ek$rI;nrn7!59~M|mzp?^_{^x_6xcgPI
z?2M&yqT+n!w8X5I%z`=bcWO4niCGHg(OEL9t&@XHdQNM`Mue=dV&hHL3dR{0hxL!F
z=B&ro<Cih%tMy?=R&%EfV!zHduo|%N^}}lJ#Lvl9Cz=d(XIt2R=CCKM_RLzbBeO>A
z4$FtdVn>z_!;)Py`8uV_+R4(fBP;XYBdb5V?>v5G#Mk;Vq*$BThvC6WU@_T|ov{y#
z!SZzKG0U4_$L_G0?DMrWRt~$*YT}d(J7YC=w&SaYe$`v2Hvg;LjBiffIhpB<&royb
z!E$A#IZJfvfYY`xhA|Cfzbr7R{MW!F$%3=*`g-NO{u<YLWIXw|S79yuH#m`TmhNo3
zv&F2{ESB@=jKxrM>H|aTYcJ)jE&FxeWigz%GVECn?E5tbhOkq{&iwzai8G!P35JPN
zubDJh9T?^|yb%$+#j+YQi7>`6-Dg_B%5eVvn?FnEtiQ9Af3N=Qldt7|y@zc6lkVR;
z&RVhGuS{?r85jQR$0^gVzh8O6uAO?~eDYQHU$6dUgEOY{{(t@buPOe`^=rvY-~PwX
z*Sh|XPyXND`JcD_-(3GYMkS#ApFd8%f4%%Sjq~jPF6aMwtpA?MnKvtk#r|5-|Nil7
z+W(r%*Yp4W?CaJ4ei{GQ6#uK7{#Vcb&FBAFtbgOX&Rht%5O5*jLcoQ93jr4bE(BZ%
zxDaq5;6lKKfC~W^0xkqx2)Gb%A>cy5g@6kI7XmH>TnM-ja3SDAz=ePd0T%);1Y8KX
z5O5*jLcoQ93jr4bE(BZ%xDaq5;6lKKfC~W^0xkqx2)Gb%A>cy5g@6kI7XmH>TnM-j
za3SDAz=ePd0T%);1Y8KX5O5*jLcoQ93jr4bE(BZ%xDaq5;6lKKfC~W^0xkqx2)Gb%
kA>cy5g@6kI7XmH>TnM-ja3SDAz=ePd0T%-Q|3cvZ08XSD=l}o!


From 502d26dd63aa2eb9febf0482dbf091d9053e9635 Mon Sep 17 00:00:00 2001
From: Shiven Mian <shivenmian97@gmail.com>
Date: Fri, 16 Feb 2024 00:30:21 -0800
Subject: [PATCH 4/9] fix: added base Skills class and env var

---
 01OS/.env.example         | 1 +
 01OS/01OS/server/teach.py | 9 +++++++++
 2 files changed, 10 insertions(+)
 create mode 100644 01OS/01OS/server/teach.py

diff --git a/01OS/.env.example b/01OS/.env.example
index f05028c..fa20ac9 100644
--- a/01OS/.env.example
+++ b/01OS/.env.example
@@ -5,6 +5,7 @@
 # else we use whisper.cpp and piper local models
 ALL_LOCAL=False
 WHISPER_MODEL_NAME="ggml-tiny.en.bin"
+TEACH_MODE=False
 
 # Uncomment to set your OpenAI API key
 # OPENAI_API_KEY=sk-...
diff --git a/01OS/01OS/server/teach.py b/01OS/01OS/server/teach.py
new file mode 100644
index 0000000..524375f
--- /dev/null
+++ b/01OS/01OS/server/teach.py
@@ -0,0 +1,9 @@
+from datetime import datetime
+
+class Skill:
+    def __init__(self, name: str):
+        self.skill_name = name
+        self.steps = []
+    
+    def teach(self, code: str):
+        self.steps.append(code)
\ No newline at end of file

From 7b3178e2fdf68780de9ce4dcc8161faa8ab8cca3 Mon Sep 17 00:00:00 2001
From: Shiven Mian <shivenmian97@gmail.com>
Date: Sat, 17 Feb 2024 05:30:05 -0800
Subject: [PATCH 5/9] feat: teach mode + accumulator fixes

---
 01OS/01OS/clients/base_device.py |  5 +--
 01OS/01OS/server/server.py       | 49 ++++++++++++++--------------
 01OS/01OS/server/teach.py        | 56 ++++++++++++++++++++++++++++++--
 01OS/01OS/utils/accumulator.py   |  6 +++-
 4 files changed, 86 insertions(+), 30 deletions(-)

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 0902a63..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()
@@ -101,8 +101,6 @@ async def websocket_endpoint(websocket: WebSocket):
     send_task = asyncio.create_task(send_messages(websocket))
     try:
         await asyncio.gather(receive_task, send_task)
-    except WebSocketDisconnect:
-        pass
     except Exception as e:
         logger.debug(traceback.format_exc())
         logger.info(f"Connection lost. Error: {e}")
@@ -290,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
index 524375f..1da969b 100644
--- a/01OS/01OS/server/teach.py
+++ b/01OS/01OS/server/teach.py
@@ -1,9 +1,59 @@
 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
 
+setup_logging()
+accumulator = Accumulator()
 class Skill:
     def __init__(self, name: str):
         self.skill_name = name
         self.steps = []
-    
-    def teach(self, code: str):
-        self.steps.append(code)
\ No newline at end of file
+
+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, 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
+
+        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"]
+                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)
+
+    print(skill.skill_name, skill.steps)
+    python_code = generate_python_code(skill.skill_name, skill.steps)
+    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:

From 566dc4df948000eae0c5ceb1185ae657c0d619c7 Mon Sep 17 00:00:00 2001
From: Shiven Mian <shivenmian97@gmail.com>
Date: Sat, 17 Feb 2024 05:33:33 -0800
Subject: [PATCH 6/9] fix: remove unnecessary files

---
 01OS/.env.example                       | 2 +-
 01OS/01OS/server/skills/Open_Spotify.py | 3 ---
 2 files changed, 1 insertion(+), 4 deletions(-)
 delete mode 100644 01OS/01OS/server/skills/Open_Spotify.py

diff --git a/01OS/.env.example b/01OS/.env.example
index c16abe8..d1749bd 100644
--- a/01OS/.env.example
+++ b/01OS/.env.example
@@ -6,7 +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=Falsegi
+TEACH_MODE=False
 
 # Uncomment to set your OpenAI API key
 # OPENAI_API_KEY=sk-...
diff --git a/01OS/01OS/server/skills/Open_Spotify.py b/01OS/01OS/server/skills/Open_Spotify.py
deleted file mode 100644
index c8e1839..0000000
--- a/01OS/01OS/server/skills/Open_Spotify.py
+++ /dev/null
@@ -1,3 +0,0 @@
-def openSpotify():
-    """Open Spotify"""
-    print(['Open Spotify app', 'Play Adele'])

From 6f540fc07c3278f2187b90ed82510f289d13428f Mon Sep 17 00:00:00 2001
From: Shiven Mian <shivenmian97@gmail.com>
Date: Sat, 17 Feb 2024 05:44:32 -0800
Subject: [PATCH 7/9] feat: add Teach Mode args

---
 01OS/start.sh | 6 ++++++
 README.md     | 4 ++++
 2 files changed, 10 insertions(+)

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.
+
 <br>
 
 ## Setup for development:

From e366f3825bce5eb8cb4731a1029987c2dc176ffe Mon Sep 17 00:00:00 2001
From: Shiven Mian <shivenmian97@gmail.com>
Date: Sat, 17 Feb 2024 13:35:53 -0800
Subject: [PATCH 8/9] feat: add code in skill instead of steps

---
 01OS/01OS/server/teach.py | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/01OS/01OS/server/teach.py b/01OS/01OS/server/teach.py
index 1da969b..ea9aa8a 100644
--- a/01OS/01OS/server/teach.py
+++ b/01OS/01OS/server/teach.py
@@ -7,6 +7,7 @@ from tkinter import messagebox
 from ..utils.accumulator import Accumulator
 import time
 import os
+import textwrap
 
 setup_logging()
 accumulator = Accumulator()
@@ -14,16 +15,18 @@ 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, steps):
+def generate_python_code(function_name, steps, code):
     code_string = f'def {to_camel_case(function_name)}():\n'
     code_string += f'    """{function_name}"""\n'
-    code_string += f'    print({steps})\n'
+    indented_code = textwrap.indent(code, '    ')
+    code_string += indented_code + '\n'
     return code_string
 
 def teach():
@@ -38,11 +41,13 @@ def teach():
         if step == "end":
             break
 
+        chunk_code = ""
         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)
@@ -50,9 +55,9 @@ def teach():
         isCorrect = messagebox.askyesno("To Proceed?", "Did I do this step right?")
         if isCorrect:
             skill.steps.append(step)
+            skill.code += chunk_code
 
-    print(skill.skill_name, skill.steps)
-    python_code = generate_python_code(skill.skill_name, skill.steps)
+    python_code = generate_python_code(skill.skill_name, skill.steps, 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:

From 6e3993d0c29665a37b8751fea36a4842572a4d23 Mon Sep 17 00:00:00 2001
From: Shiven Mian <shivenmian97@gmail.com>
Date: Sat, 17 Feb 2024 14:01:52 -0800
Subject: [PATCH 9/9] feat: add function to add steps instead, force task
 completion

---
 01OS/01OS/server/teach.py | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/01OS/01OS/server/teach.py b/01OS/01OS/server/teach.py
index ea9aa8a..78f7a88 100644
--- a/01OS/01OS/server/teach.py
+++ b/01OS/01OS/server/teach.py
@@ -22,13 +22,19 @@ def to_camel_case(text):
     camel_case_string = words[0].lower() + ''.join(word.title() for word in words[1:])
     return camel_case_string
 
-def generate_python_code(function_name, steps, code):
+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()
@@ -42,6 +48,8 @@ def teach():
             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"]
@@ -57,7 +65,10 @@ def teach():
             skill.steps.append(step)
             skill.code += chunk_code
 
-    python_code = generate_python_code(skill.skill_name, skill.steps, skill.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: