pull/5/head
killian 11 months ago
parent 64a0d044ae
commit 70f1667bcc

@ -0,0 +1,99 @@
"""
Exposes a POST endpoint called /computer. Things from there go into the queue.
Exposes a ws endpoint called /user. Things from there go into the queue. We also send things in the queue back (that are role: assistant)
In a while loop we watch the queue.
"""
import json
import time
import queue
import os
from threading import Thread
import uvicorn
from fastapi import FastAPI
from threading import Thread
from starlette.websockets import WebSocket
from create_interpreter import create_interpreter
# Create interpreter
interpreter = create_interpreter()
script_dir = os.path.dirname(os.path.abspath(__file__))
conversation_history_path = os.path.join(script_dir, 'conversations', 'user.json')
# Create Queue objects
to_user = queue.Queue()
to_assistant = queue.Queue()
app = FastAPI()
@app.post("/computer")
async def read_computer(item: dict):
to_assistant.put(item)
return {"message": "Item added to queue"}
@app.websocket("/user")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_json()
to_assistant.put(data)
if not to_user.empty():
message = to_user.get()
await websocket.send_json(message)
audio_chunks = []
def queue_listener():
while True:
# Check 10x a second for new messages
while to_assistant.empty():
time.sleep(0.1)
message = to_assistant.get()
# Hold the audio in a buffer. If it's ready (we got end flag, stt it)
if message["type"] == "audio":
if "content" in message:
audio_chunks.append(message)
if "end" in message:
text = stt(audio_chunks)
audio_chunks = []
message = {"role": "user", "type": "message", "content": text}
else:
continue
# Custom stop message will halt us
if message.get("content") and message.get("content").lower().strip(".,!") == "stop":
continue
# Load, append, and save conversation history
with open(conversation_history_path, 'r') as file:
messages = json.load(file)
messages.append(message)
with open(conversation_history_path, 'w') as file:
json.dump(messages, file)
for chunk in interpreter.chat(messages):
# Send it to the interface
to_user.put(chunk)
# Stream audio chunks
# If we have a new message, save our progress and go back to the top
if not to_assistant.empty():
with open(conversation_history_path, 'w') as file:
json.dump(interpreter.messages, file)
break
# Create a thread for the queue listener
queue_thread = Thread(target=queue_listener)
# Start the queue listener thread
queue_thread.start()
# Run the FastAPI app
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)

@ -1,38 +0,0 @@
import redis
import json
import time
# Set up Redis connection
r = redis.Redis(host='localhost', port=6379, db=0)
def main(interpreter):
while True:
# Check 10x a second for new messages
message = None
while message is None:
message = r.lpop('to_core')
time.sleep(0.1)
# Custom stop message will halt us
if message.get("content") and message.get("content").lower().strip(".,!") == "stop":
continue
# Load, append, and save conversation history
with open("conversations/user.json", "r") as file:
messages = json.load(file)
messages.append(message)
with open("conversations/user.json", "w") as file:
json.dump(messages, file)
for chunk in interpreter.chat(messages):
# Send it to the interface
r.rpush('to_interface', chunk)
# If we have a new message, save our progress and go back to the top
if r.llen('to_main') > 0:
with open("conversations/user.json", "w") as file:
json.dump(interpreter.messages, file)
break

@ -0,0 +1,127 @@
from interpreter import interpreter
import os
import glob
import json
import requests
def create_interpreter():
### SYSTEM MESSAGE
# The system message is where most of the 01's behavior is configured.
# You can put code into the system message {{ in brackets like this }} which will be rendered just before the interpreter starts writing a message.
system_message = """
You are an executive assistant AI that helps the user manage their tasks. You can run Python code.
Store the user's tasks in a Python list called `tasks`.
---
The user's current task is: {{ tasks[0] if tasks else "No current tasks." }}
{{
if len(tasks) > 1:
print("The next task is: ", tasks[1])
}}
---
When the user completes the current task, you should remove it from the list and read the next item by running `tasks = tasks[1:]\ntasks[0]`. Then, tell the user what the next task is.
When the user tells you about a set of tasks, you should intelligently order tasks, batch similar tasks, and break down large tasks into smaller tasks (for this, you should consult the user and get their permission to break it down). Your goal is to manage the task list as intelligently as possible, to make the user as efficient and non-overwhelmed as possible. They will require a lot of encouragement, support, and kindness. Don't say too much about what's ahead of them just try to focus them on each step at a time.
After starting a task, you should check in with the user around the estimated completion time to see if the task is completed. Use the `schedule(datetime, message)` function, which has already been imported.
To do this, schedule a reminder based on estimated completion time using the function `schedule(datetime_object, "Your message here.")`, WHICH HAS ALREADY BEEN IMPORTED. YOU DON'T NEED TO IMPORT THE `schedule` FUNCTION. IT IS AVALIABLE. You'll recieve the message at `datetime_object`.
You guide the user through the list one task at a time, convincing them to move forward, giving a pep talk if need be. Your job is essentially to answer "what should I (the user) be doing right now?" for every moment of the day.
Remember: You can run Python code. Be very concise. Ensure that you actually run code every time! THIS IS IMPORTANT. You NEED to write code. **Help the user by being very concise in your answers.** Do not break down tasks excessively, just into simple, few minute steps. Don't assume the user lives their life in a certain way— pick very general tasks if you're breaking a task down.
""".strip()
interpreter.custom_instructions = system_message
### LLM SETTINGS
# Local settings
# interpreter.llm.model = "local"
# interpreter.llm.api_base = "https://localhost:8080/v1" # Llamafile default
# interpreter.llm.max_tokens = 1000
# interpreter.llm.context_window = 3000
# Hosted settings
interpreter.llm.api_key = os.getenv('OPENAI_API_KEY')
interpreter.llm.model = "gpt-4"
interpreter.auto_run = True
interpreter.force_task_completion = True
### MISC SETTINGS
interpreter.offline = True
interpreter.id = 206 # Used to identify itself to other interpreters. This should be changed programatically so it's unique.
### RESET conversations/user.json
script_dir = os.path.dirname(os.path.abspath(__file__))
user_json_path = os.path.join(script_dir, 'conversations', 'user.json')
with open(user_json_path, 'w') as file:
json.dump([], file)
### CONNECT TO /run
class Python:
"""
This class contains all requirements for being a custom language in Open Interpreter:
- name (an attribute)
- run (a method)
- stop (a method)
- terminate (a method)
"""
# This is the name that will appear to the LLM.
name = "python"
def run(self, code):
"""Generator that yields a dictionary in LMC Format."""
# Prepare the data
data = {"language": "python", "code": code}
# Send the data to the /run endpoint
response = requests.post("http://localhost:8000/run", json=data, stream=True)
# Stream the response
for line in response.iter_lines():
if line: # filter out keep-alive new lines
yield json.loads(line)
def stop(self):
"""Stops the code."""
# Not needed here, because e2b.run_code isn't stateful.
pass
def terminate(self):
"""Terminates the entire process."""
# Not needed here, because e2b.run_code isn't stateful.
pass
interpreter.computer.languages = [Python]
### SKILLS
script_dir = os.path.dirname(os.path.abspath(__file__))
skills_dir = os.path.join(script_dir, 'skills')
for file in glob.glob(os.path.join(skills_dir, '*.py')):
with open(file, 'r') as f:
for chunk in interpreter.computer.run("python", f.read()):
print(chunk)
### RETURN INTERPRETER
return interpreter

@ -1,90 +0,0 @@
import redis
import RPi.GPIO as GPIO
import asyncio
import websockets
import sounddevice as sd
import numpy as np
import time
import re
from stt import stt
from tts import tts
# Set up Redis connection
r = redis.Redis(host='localhost', port=6379, db=0)
# Set up websocket connection
websocket = websockets.connect('ws://localhost:8765')
# This is so we only say() full sentences
accumulated_text = ""
def is_full_sentence(text):
return text.endswith(('.', '!', '?'))
def split_into_sentences(text):
return re.split(r'(?<=[.!?])\s+', text)
async def send_to_websocket(message):
async with websocket as ws:
await ws.send(message)
async def check_websocket():
async with websocket as ws:
message = await ws.recv()
return message
def main():
while True:
# If the button is pushed down
if not GPIO.input(18):
# Tell websocket and core that the user is speaking
send_to_websocket({"role": "user", "type": "message", "start": True}) # Standard start flag, required per streaming LMC protocol (https://docs.openinterpreter.com/guides/streaming-response)
r.rpush('to_core', {"role": "user", "type": "message", "content": "stop"}) # Custom stop message. Core is not streaming LMC (it's static LMC) so doesn't require that ^ flag
# Record audio from the microphone in chunks
audio_chunks = []
# Continue recording until the button is released
while not GPIO.input(18):
chunk = sd.rec(int(chunk_duration * sample_rate), samplerate=sample_rate, channels=2)
sd.wait() # Wait until recording is finished
audio_chunks.append(chunk)
# Transcribe
text = transcribe(audio_chunks)
message = {"role": "user", "type": "message", "content": text, "time": time.time()}
# Send message to core and websocket
r.rpush('to_core', message)
send_to_websocket(message)
# Send user message end flag to websocket, required per streaming LMC protocol
send_to_websocket({"role": "user", "type": "message", "end": True})
# Send out anything in the to_interface queue
chunk = r.lpop('to_interface')
if chunk:
send_to_websocket(chunk)
accumulated_text += chunk["content"]
# Speak full sentences out loud
sentences = split_into_sentences(accumulated_text)
if is_full_sentence(sentences[-1]):
for sentence in sentences:
say(sentence)
accumulated_text = ""
else:
for sentence in sentences[:-1]:
say(sentence)
accumulated_text = sentences[-1]
else:
say(accumulated_text)
accumulated_text = ""
message = check_websocket()
if message:
r.rpush('to_core', message)
if __name__ == "__main__":
main()

@ -1,88 +0,0 @@
"""
Starts the assistant, which includes Open Interpreter.
"""
from assistant import main
from interpreter import interpreter
import os
import glob
import json
### SYSTEM MESSAGE
# The system message is where most of the 01's behavior is configured.
# You can put code into the system message {{ in brackets like this }} which will be rendered just before the interpreter starts writing a message.
system_message = """
You are an executive assistant AI that helps the user manage their tasks. You can run Python code.
Store the user's tasks in a Python list called `tasks`.
---
The user's current task is: {{ tasks[0] if tasks else "No current tasks." }}
{{
if len(tasks) > 1:
print("The next task is: ", tasks[1])
}}
---
When the user completes the current task, you should remove it from the list and read the next item by running `tasks = tasks[1:]\ntasks[0]`. Then, tell the user what the next task is.
When the user tells you about a set of tasks, you should intelligently order tasks, batch similar tasks, and break down large tasks into smaller tasks (for this, you should consult the user and get their permission to break it down). Your goal is to manage the task list as intelligently as possible, to make the user as efficient and non-overwhelmed as possible. They will require a lot of encouragement, support, and kindness. Don't say too much about what's ahead of them just try to focus them on each step at a time.
After starting a task, you should check in with the user around the estimated completion time to see if the task is completed. Use the `schedule(datetime, message)` function, which has already been imported.
To do this, schedule a reminder based on estimated completion time using the function `schedule(datetime_object, "Your message here.")`, WHICH HAS ALREADY BEEN IMPORTED. YOU DON'T NEED TO IMPORT THE `schedule` FUNCTION. IT IS AVALIABLE. You'll recieve the message at `datetime_object`.
You guide the user through the list one task at a time, convincing them to move forward, giving a pep talk if need be. Your job is essentially to answer "what should I (the user) be doing right now?" for every moment of the day.
Remember: You can run Python code. Be very concise. Ensure that you actually run code every time! THIS IS IMPORTANT. You NEED to write code. **Help the user by being very concise in your answers.** Do not break down tasks excessively, just into simple, few minute steps. Don't assume the user lives their life in a certain way— pick very general tasks if you're breaking a task down.
""".strip()
interpreter.custom_instructions = system_message
### TOOLS
for file in glob.glob('interpreter/tools/*.py'):
with open(file, 'r') as f:
for chunk in interpreter.computer.run("python", f.read()):
print(chunk)
### LLM SETTINGS
# Local settings
# interpreter.llm.model = "local"
# interpreter.llm.api_base = "https://localhost:8080/v1" # Llamafile default
# interpreter.llm.max_tokens = 1000
# interpreter.llm.context_window = 3000
# Hosted settings
interpreter.llm.api_key = os.getenv('OPENAI_API_KEY')
interpreter.llm.model = "gpt-4-0125-preview"
interpreter.auto_run = True
# interpreter.force_task_completion = True
### MISC SETTINGS
interpreter.offline = True
interpreter.id = 206 # Used to identify itself to other interpreters. This should be changed programatically so it's unique.
### RESET conversations/user.json
script_dir = os.path.dirname(os.path.abspath(__file__))
user_json_path = os.path.join(script_dir, 'conversations', 'user.json')
with open(user_json_path, 'w') as file:
json.dump([], file)
### START ASSISTANT
main(interpreter)
Loading…
Cancel
Save