diff --git a/01OS/.cursorignore b/01OS/.cursorignore new file mode 100644 index 0000000..b494a46 --- /dev/null +++ b/01OS/.cursorignore @@ -0,0 +1,4 @@ +_archive +__pycache__ +.idea + diff --git a/01OS/.env.example b/01OS/.env.example index 5b27224..19a8c68 100644 --- a/01OS/.env.example +++ b/01OS/.env.example @@ -16,16 +16,16 @@ TEACH_MODE=False PIPER_VOICE_URL="https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/" PIPER_VOICE_NAME="en_US-lessac-medium.onnx" -# Expose through Ngrok -# Uncomment following line with your Ngrok auth token (https://dashboard.ngrok.com/get-started/your-authtoken) -#NGROK_AUTHTOKEN="AUTH TOKEN" - # If SERVER_START, this is where we'll serve the server. # If CLIENT_START, this is where the client expects the server to be. +# SERVER_URL=ws://localhost:8000/ SERVER_URL=ws://0.0.0.0:8000/ SERVER_START=True CLIENT_START=True +# The port to start the local server on +SERVER_LOCAL_PORT=8000 + # Explicitly set the client type (macos, rpi) CLIENT_TYPE=auto @@ -34,9 +34,6 @@ CODE_RUNNER=server TTS_RUNNER=server # If client, audio will be sent over websocket. STT_RUNNER=client # If server, audio will be sent over websocket. -# Will expose the server publically and display that URL. -SERVER_EXPOSE_PUBLICALLY=False - # Image capture settings CAMERA_ENABLED=False diff --git a/01OS/01OS/server/server.py b/01OS/01OS/server/server.py index 7cb71b0..a78a42e 100644 --- a/01OS/01OS/server/server.py +++ b/01OS/01OS/server/server.py @@ -18,7 +18,6 @@ import urllib.parse from .utils.kernel import put_kernel_messages_into_queue 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 @@ -31,6 +30,9 @@ app = FastAPI() conversation_history_path = Path(__file__).parent / 'conversations' / 'user.json' +SERVER_LOCAL_PORT = int(os.getenv('SERVER_LOCAL_PORT', 8000)) + + # This is so we only say() full sentences def is_full_sentence(text): return text.endswith(('.', '!', '?')) @@ -318,18 +320,6 @@ async def stream_tts_to_device(sentence): return for chunk in stream_tts(sentence): await to_device.put(chunk) - -async def setup_ngrok(ngrok_auth_token, parsed_url): - # Set up Ngrok - logger.info("Setting up Ngrok") - ngrok_listener = await ngrok.forward(f"{parsed_url.hostname}:{parsed_url.port}", authtoken=ngrok_auth_token) - ngrok_parsed_url = urllib.parse.urlparse(ngrok_listener.url()) - - # Setup SERVER_URL environment variable for device to use - connection_url = f"wss://{ngrok_parsed_url.hostname}/" - logger.info(f"Ngrok established at {ngrok_parsed_url.geturl()}") - logger.info(f"\033[1mSERVER_CONNECTION_URL should be set to \"{connection_url}\"\033[0m") - from uvicorn import Config, Server @@ -346,20 +336,11 @@ if __name__ == "__main__": # 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`...") + # Start the server + logger.info("Starting `server.py`... on localhost:" + str(SERVER_LOCAL_PORT)) - config = Config(app, host=parsed_url.hostname, port=parsed_url.port, lifespan='on') + config = Config(app, host="localhost", port=SERVER_LOCAL_PORT, lifespan='on') server = Server(config) await server.serve() diff --git a/01OS/README.md b/01OS/README.md deleted file mode 100644 index 9b773c8..0000000 --- a/01OS/README.md +++ /dev/null @@ -1,33 +0,0 @@ -The open-source language model computer. - -```bash -pip install 01OS -``` - -```bash -01 # This will run a server + attempt to determine and run a client. -# (Behavior can be modified by changing the contents of `.env`) -``` - -**Expose an 01 server publically:** - -```bash -01 --server --expose # This will print a URL that a client can point to. -``` - -**Run a specific client:** - -```bash -01 --client macos # Options: macos, rpi -``` - -**Run locally:** - -The current default uses OpenAI's services. - -The `--local` flag will install and run the [whisper.cpp](https://github.com/ggerganov/whisper.cpp) STT and [Piper](https://github.com/rhasspy/piper) TTS models. - -```bash -01 --local # Local client and server -01 --local --server --expose # Expose a local server -``` diff --git a/01OS/start.sh b/01OS/start.sh index d3901d3..1a73fa7 100755 --- a/01OS/start.sh +++ b/01OS/start.sh @@ -56,8 +56,22 @@ fi # Check if "--expose" is passed as an argument if [[ "$@" == *"--expose"* ]]; then - # If "--expose" is passed, set SERVER_EXPOSE_PUBLICALLY to True - export SERVER_EXPOSE_PUBLICALLY="True" + if [[ "$SERVER_START" != "True" ]]; then + echo "Error: Start script must be started with --serve for tunneling to work." + exit 1 + else + export TUNNEL_START="True" + + if [[ "$@" == *"--expose-with-bore"* ]]; then + export TUNNEL_METHOD="bore" + elif [[ "$@" == *"--expose-with-localtunnel"* ]]; then + export TUNNEL_METHOD="localtunnel" + elif [[ "$@" == *"--expose-with-ngrok"* ]]; then + export TUNNEL_METHOD="ngrok" + fi + + echo "exposing server" + fi fi # Check if "--clear-local" is passed as an argument @@ -177,6 +191,14 @@ start_server() { echo "Server started as process $SERVER_PID" } +# Function to start tunnel service +start_tunnel() { + echo "Starting tunnel..." + ./tunnel.sh & + TUNNEL_PID=$! + echo "Tunnel started as process $TUNNEL_PID" +} + stop_processes() { if [[ -n $CLIENT_PID ]]; then echo "Stopping client..." @@ -186,6 +208,10 @@ stop_processes() { echo "Stopping server..." kill $SERVER_PID fi + if [[ -n $TUNNEL_PID ]]; then + echo "Stopping tunnel..." + kill $TUNNEL_PID + fi } # Trap SIGINT and SIGTERM to stop processes when the script is terminated @@ -203,9 +229,16 @@ if [[ "$CLIENT_START" == "True" ]]; then start_client fi +# TUNNEL +# Start tunnel if TUNNEL_START is True +if [[ "$TUNNEL_START" == "True" ]]; then + start_tunnel +fi + # Wait for client and server processes to exit wait $CLIENT_PID wait $SERVER_PID +wait $TUNNEL_PID # TTS, STT diff --git a/01OS/tunnel.sh b/01OS/tunnel.sh new file mode 100755 index 0000000..564acf9 --- /dev/null +++ b/01OS/tunnel.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +# Get tunnel method from TUNNEL_METHOD environment variable, but default to bore +# Possible options; +# - bore +# - localtunnel +# - ngrok +TUNNEL_METHOD=${TUNNEL_METHOD:-bore} + +# Get the SERVER_PORT environment variable, but default to 8000 +SERVER_LOCAL_PORT=${SERVER_LOCAL_PORT:-8000} + +echo "Using $TUNNEL_METHOD to expose port $SERVER_LOCAL_PORT on localhost..." + +# If the TUNNEL_METHOD is bore, then we need to check if the bore-cli is installed +if [[ "$TUNNEL_METHOD" == "bore" ]]; then + + if ! command -v bore &> /dev/null; then + echo "The bore-cli command is not available. Please run 'cargo install bore-cli'." + echo "For more information, see https://github.com/ekzhang/bore" + exit 1 + else + bore local $SERVER_LOCAL_PORT --to bore.pub | while IFS= read -r line; do + if [[ "$line" == *"listening at bore.pub:"* ]]; then + remote_port=$(echo "$line" | grep -o 'bore.pub:[0-9]*' | cut -d':' -f2) + echo "Please set your client env variable for SERVER_URL=ws://bore.pub:$remote_port" + break + fi + done + fi + +elif [[ "$TUNNEL_METHOD" == "localtunnel" ]]; then + + if ! command -v lt &> /dev/null; then + echo "The 'lt' command is not available." + echo "Please ensure you have Node.js installed, then run 'npm install -g localtunnel'." + echo "For more information, see https://github.com/localtunnel/localtunnel" + exit 1 + else + npx localtunnel --port $SERVER_LOCAL_PORT | while IFS= read -r line; do + if [[ "$line" == *"your url is: https://"* ]]; then + remote_url=$(echo "$line" | grep -o 'https://[a-zA-Z0-9.-]*' | sed 's|https://||') + echo "Please set your client env variable for SERVER_URL=wss://$remote_url" + break + fi + done + fi + +elif [[ "$TUNNEL_METHOD" == "ngrok" ]]; then + + if ! command -v ngrok &> /dev/null; then + echo "The ngrok command is not available." + echo "Please install ngrok using the instructions at https://ngrok.com/docs/getting-started/" + exit 1 + else + ngrok http $SERVER_LOCAL_PORT --log stdout | while IFS= read -r line; do + if [[ "$line" == *"started tunnel"* ]]; then + remote_url=$(echo "$line" | grep -o 'https://[a-zA-Z0-9.-]*' | sed 's|https://||') + echo "Please set your client env variable for SERVER_URL=wss://$remote_url" + break + fi + done + fi + +fi diff --git a/README.md b/README.md index fd5fdb3..0b957cb 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,40 @@ pip install 01OS # (Behavior can be modified by changing the contents of `.env`) ``` -**Expose an 01 server publically:** +**Expose an 01 Server Publicly** + +We currently support exposing the 01 server publicly via a couple of different tunnel services: + +- **bore.pub** ([GitHub](https://github.com/ekzhang/bore)) + - **Requirements:** Ensure that Rust is installed ([Rust Installation](https://www.rust-lang.org/tools/install)), then run: + ``` + cargo install bore-cli + ``` + - **To Expose:** + ```bash + 01 --server --expose-with-bore + ``` + +- **localtunnel** ([GitHub](https://github.com/localtunnel/localtunnel)) + - **Requirements:** Ensure that Node.js is installed ([Node.js Download](https://nodejs.org/en/download)), then run: + ``` + npm install -g localtunnel + ``` + - **To Expose:** + ```bash + 01 --server --expose-with-localtunnel + ``` + +- **ngrok** ([Website](https://ngrok.com/)) + - **Requirements:** Install ngrok ([Getting Started with ngrok](https://ngrok.com/docs/getting-started/)), and set up an ngrok account. Get your auth key from [ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken), then set it in your local configuration by running: + ``` + ngrok config add-authtoken your_auth_token_here + ``` + - **To Expose:** + ```bash + 01 --server --expose-with-ngrok + ``` -```bash -01 --server --expose # This will print a URL that a client can point to. -``` **Run a specific client:** @@ -55,7 +84,7 @@ The `--local` flag will install and run the [whisper.cpp](https://github.com/gge ```bash 01 --local # Local client and server -01 --local --server --expose # Expose a local server +01 --local --server --expose-with-bore # Expose the local server with a public tunnel ``` **Teach Mode (experimental)**