From 02924614ade8b69185cec117175562bfe8eca870 Mon Sep 17 00:00:00 2001 From: ALIHAN DIKEL Date: Thu, 18 May 2023 23:34:55 +0300 Subject: [PATCH] implement brain api service & implement chatui with gradio & refactor brain class for api and manage.sh --- .gitignore | 3 +- README.md | 26 ++++--- brain/brain_base.py | 46 +++++++++++-- brain/brain_openai.py | 36 ++-------- brain/cli.py | 2 +- brain/service_gradio.py | 24 +++---- manage.sh | 53 ++++++++------ requirements.txt | 149 ++++++++++++++++++++++++++++++++++++++++ settings/admin.yml | 17 +++-- 9 files changed, 266 insertions(+), 90 deletions(-) diff --git a/.gitignore b/.gitignore index c29e49f..a67bf01 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ pids/ logs/ .ipynb_checkpoints packages -*.ipynb \ No newline at end of file +*.ipynb +.idea \ No newline at end of file diff --git a/README.md b/README.md index 60e7519..d708bd7 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,22 @@ # Preparations before Flight + +## Hardware setup * turn uadis hotpot on * plugin xiaomi repeater "_plus" to powers source * connect rpi to power source * wait for boot and ams vpn connection * until connected devices @uadis increment +1 with device name "uad" -* manual setup: - * ssh into - * cd tello-commander - * source venv/bin/activate - * ./manage.sh connect-drone - * ifconfig: check if wlan1 is up - * ./manage.sh start-commander - * start drone - * until repeater lights blue without blinking - * ./manage.sh get-dhcp - * ./manage start-talikng +### manual cli-rpi setup: + * ssh into + * cd tello-commander + * source venv/bin/activate + * ./manage.sh connect-drone + * ifconfig: check if wlan1 is up + * ./manage.sh start-commander + * start drone + * until repeater lights blue without blinking + * ./manage.sh get-dhcp + * ./manage start-talikng + +### auto chatui-rpi setup: diff --git a/brain/brain_base.py b/brain/brain_base.py index c4d3e15..8a0be07 100644 --- a/brain/brain_base.py +++ b/brain/brain_base.py @@ -1,7 +1,10 @@ +import ast +import json import os -from commander.commands import CommandHandler +from loguru import logger +from commander.commands import CommandHandler class BaseBrain: @@ -20,13 +23,48 @@ class BaseBrain: with open(prompt_filepath, "r") as f: return f.read() + def _is_valid_json(self, answer): + try: + response_json = json.loads(answer) + return True + except ValueError as e: + logger.error(f"chatgpt failed to return json obj: {answer}") + return False + + def _gc(self): + self.cmd_prompt = None + self.response = None + def is_emergency(self, input): if input == "q": - print("##### BASE BRAIN: EMERGENCY STOP DETECTED!!! #####") + msg = "##### BASE BRAIN: EMERGENCY STOP DETECTED!!! #####" + logger.warning(msg) + self.response_to_chatui = msg self.command_handler.handle({"command": "emergency"}) return True else: return False - def listen(self): - self.cmd_prompt = input("\n\nwhat should I do now?\n(enter q for emergency)\n\t") + def listen(self, channel="cli", prompt=None): + if channel == "cli": + self.cmd_prompt = input("\n\nwhat should I do now?\n(enter q for emergency)\n\t") + elif channel == "api": + self.cmd_prompt = prompt + + def command(self): + if self._is_valid_json(self.answer): + command = ast.literal_eval(self.answer) + if command == {}: + msg = f"I failed to understand: {command}" + logger.warning(msg) + self.response_to_chatui = msg + else: + msg = f"I will send this command: {command}" + logger.success(msg) + self.response_to_chatui = msg + self.command_handler.handle(command) + else: + msg = f"\tI will skip this:\n {self.answer}" + logger.warning(msg) + self.response_to_chatui = msg + self._gc() \ No newline at end of file diff --git a/brain/brain_openai.py b/brain/brain_openai.py index 0635467..7c60f72 100644 --- a/brain/brain_openai.py +++ b/brain/brain_openai.py @@ -1,10 +1,9 @@ import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -import json -import ast import openai +from loguru import logger from brain_base import BaseBrain from settings.config import settings @@ -13,48 +12,25 @@ from settings.config import settings class CloudSTTBrain: def __init__(self): - print("not implemented") + logger.error("not implemented") class CloudChatBrain(BaseBrain): - def __init__(self): super().__init__() openai.api_key = settings.OPENAI_API_KEY - def _is_valid_json(self, answer): - try: - response_json = json.loads(answer) - return True - except ValueError as e: - print(f"chatgpt failed to return json obj: {answer}") - return False - - def _gc(self): - self.cmd_prompt = None - self.response = None - def understand(self): + ## BURADA TRY:EXCEPT BLOCKU GEREKİR self.response = openai.ChatCompletion.create( model="gpt-3.5-turbo", - temperature=0.2, + temperature=0.3, messages=[ {"role": "system", "content": self.sys_prompt}, {"role": "user", "content": self.cmd_prompt} ]) - - def command(self): - answer = self.response.choices[0].message.content - if self._is_valid_json(answer): - command = ast.literal_eval(answer) - if command == {}: - print(f"I failed to understand: {command}") - else: - print(f"I will send this command: {command}") - self.command_handler.handle(command) - else: - print(f"\tI will skip this:\n {command}") - self._gc() + self.answer = self.response.choices[0].message.content + diff --git a/brain/cli.py b/brain/cli.py index cfb2868..c1fe71b 100644 --- a/brain/cli.py +++ b/brain/cli.py @@ -5,7 +5,7 @@ brain = CloudChatBrain() while True: try: - brain.listen() + brain.listen(channel="cli") if not brain.is_emergency(brain.cmd_prompt): brain.understand() brain.command() diff --git a/brain/service_gradio.py b/brain/service_gradio.py index d49872b..ef0bf40 100644 --- a/brain/service_gradio.py +++ b/brain/service_gradio.py @@ -3,31 +3,31 @@ import random import gradio as gr from loguru import logger +from brain.brain_openai import CloudChatBrain -def make_completion(history): - return "ok" - -def answer(input, history): - history.append({"role": "user", "content": input}) - response = random.choice(["How are you?", "I love you", "I'm very hungry"]) - history.append({"role": "assistant", "content": response}) +def brain_commander(prompt, history): + brain.listen(channel="api", prompt=prompt) + brain.understand() + history.append({"role": "user", "content": prompt}) + history.append({"role": "assistant", "content": brain.answer}) messages = [(history[i]["content"], history[i+1]["content"]) for i in range(0, len(history)-1, 2)] - logger.debug(history) + brain.command() return messages, history +brain = CloudChatBrain() with gr.Blocks() as demo: - chatbot = gr.Chatbot(label="Fly my drone with chatGPT as copilot") + chatbot_ui = gr.Chatbot(label="drone flight with chatgpt as copilot") state = gr.State([]) with gr.Row(): prompt = gr.Textbox( show_label=True, - label="what should I do now?\n(enter q for emergency)", - placeholder="Enter text and press enter")\ + label="what should I do now? (enter q for emergency)", + placeholder="Enter flight command and press enter")\ .style(container=True) - prompt.submit(answer, [prompt, state], [chatbot, state]) + prompt.submit(brain_commander, [prompt, state], [chatbot_ui, state]) demo.launch(server_name="0.0.0.0", server_port=8890, debug=True) \ No newline at end of file diff --git a/manage.sh b/manage.sh index 21368fc..d79a2b0 100755 --- a/manage.sh +++ b/manage.sh @@ -5,11 +5,11 @@ ###################### ## SCRIPT INIT ###################### -DRONE_INTERFACE=$(yq '.commander.drone_interface' < settings/admin.yml) -DRONE_WPA_SUPP_CONF=$(yq '.commander.drone_wpa_supp' < settings/admin.yml) -NET_INTERFACE=$(yq '.commander.net_interface' < settings/admin.yml) -NET_WPA_SUPP_CONF=$(yq '.commander.net_wpa_supp' < settings/admin.yml) -#ENV_FOR_DYNACONF=$(yq '.commander.env_for_dynaconf' < settings/admin.yml) +DRONE_INTERFACE=$(yq '.drone_interface' < settings/admin.yml) +DRONE_WPA_SUPP_CONF=$(yq '.drone_wpa_supp' < settings/admin.yml) +NET_INTERFACE=$(yq '.net_interface' < settings/admin.yml) +NET_WPA_SUPP_CONF=$(yq '.net_wpa_supp' < settings/admin.yml) +#ENV_FOR_DYNACONF=$(yq '.env_for_dynaconf' < settings/admin.yml) pids_dir='./pids' if [[ ! -d "$pids_dir" ]]; then @@ -34,13 +34,6 @@ connect_using_wpa_supp() { sudo wpa_supplicant -D nl80211 -i $DRONE_INTERFACE -c network/$DRONE_WPA_SUPP_CONF } -#wait_for_drone() { - #while ! ping -c1 192.168.10.1 &>/dev/null; do - # echo "Drone is offline. Waiting.."; sleep 2 - #done - #echo "Drone is available, can ask for dhcp"; sleep 1 -#} - get_dhcp_ip () { while true; do @@ -70,11 +63,12 @@ start_commander_service() { } start_brain_service() { - venv/bin/python brain/service.py $1 + venv/bin/python -m gradio brain/service.py $1 } -talk_to_drone() { - ENV_FOR_DYNACONF=$1 python brain/cli.py +talk_to_drone_via_cli() { + commander_host=$1 + ENV_FOR_DYNACONF=$commander_host python brain/cli.py } kill_everything() { @@ -172,11 +166,25 @@ elif [ "$1" == "start-commander" ]; then elif [ "$1" == "stop-commander" ]; then commander_pid_file="$pids_dir/commander_pid.txt" if [ -f "$commander_pid_file" ]; then - sudo killport 8889 sudo pkill -15 -P $(cat $commander_pid_file) + sudo killport 8889 echo "stopped commander" fi +elif [ "$1" == "start-brain" ]; then + start_brain_service $2 > logs/brain.log 2>&1 & + brain_pid=$! + echo "started brain service with PID brain_pid" + echo brain_pid > $pids_dir/brain_pid.txt + +elif [ "$1" == "stop-brain" ]; then + brain_pid_file="$pids_dir/brain_pid.txt" + if [ -f "brain_pid_file" ]; then + sudo pkill -15 -P $(cat brain_pid_file) + sudo killport 8890 + echo "stopped brain service" + fi + elif [ "$1" == "prepare-flight" ]; then ./manage.sh connect-drone ./manage.sh get-dhcp @@ -189,8 +197,8 @@ elif [ "$1" == "finish-flight" ]; then ./manage.sh stop-commander kill_everything echo "flight finished" -elif [ "$1" == "start-talking" ]; then - talk_to_drone $2 +elif [ "$1" == "start-clitalk" ]; then + talk_to_drone_via_cli $2 ###################### ## INFO @@ -201,10 +209,11 @@ else - list-wifis - connect-/ disconnect-drone - get-/ kill-dhcp - - start-/ stop-jupyter - - start-/ stop-cs - - start-/ stop-commander [port] - - start-talking tuncel / commander + - start-/ stop-jupyter (8888) + - start-/ stop-cs (8888) + - start-/ stop-commander [port (8889)] + - start-/ stop-brain [port (8890)] + - start-clitalk [host (tuncel/commander)] - turn-off - prepare-/ finish-flight" fi diff --git a/requirements.txt b/requirements.txt index e69de29..4e3bfd6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,149 @@ +aiofiles==22.1.0 +aiohttp==3.8.4 +aiosignal==1.3.1 +aiosqlite==0.19.0 +altair==5.0.0 +anyio==3.6.2 +argon2-cffi==21.3.0 +argon2-cffi-bindings==21.2.0 +arrow==1.2.3 +asttokens==2.2.1 +async-timeout==4.0.2 +attrs==23.1.0 +Babel==2.12.1 +backcall==0.2.0 +backoff==2.2.1 +beautifulsoup4==4.12.2 +bleach==6.0.0 +certifi==2023.5.7 +cffi==1.15.1 +charset-normalizer==3.1.0 +click==8.1.3 +comm==0.1.3 +contourpy==1.0.7 +cycler==0.11.0 +debugpy==1.6.7 +decorator==5.1.1 +defusedxml==0.7.1 +djitellopy==2.4.0 +dynaconf==3.1.12 +executing==1.2.0 +fastapi==0.95.1 +fastjsonschema==2.16.3 +ffmpy==0.3.0 +filelock==3.12.0 +fonttools==4.39.4 +fqdn==1.5.1 +frozenlist==1.3.3 +fsspec==2023.5.0 +gradio==3.31.0 +gradio_client==0.2.5 +h11==0.14.0 +httpcore==0.17.1 +httptools==0.5.0 +httpx==0.24.0 +huggingface-hub==0.14.1 +idna==3.4 +ipykernel==6.23.0 +ipython==8.13.2 +ipython-genutils==0.2.0 +isoduration==20.11.0 +jedi==0.18.2 +Jinja2==3.1.2 +json5==0.9.11 +jsonpointer==2.3 +jsonschema==4.17.3 +jupyter-events==0.6.3 +jupyter-ydoc==0.2.4 +jupyter_client==8.2.0 +jupyter_core==5.3.0 +jupyter_server==2.5.0 +jupyter_server_fileid==0.9.0 +jupyter_server_terminals==0.4.4 +jupyter_server_ydoc==0.8.0 +jupyterlab==3.6.3 +jupyterlab-pygments==0.2.2 +jupyterlab_server==2.22.1 +kiwisolver==1.4.4 +linkify-it-py==2.0.2 +loguru==0.7.0 +markdown-it-py==2.2.0 +MarkupSafe==2.1.2 +matplotlib==3.7.1 +matplotlib-inline==0.1.6 +mdit-py-plugins==0.3.3 +mdurl==0.1.2 +mistune==2.0.5 +multidict==6.0.4 +nbclassic==1.0.0 +nbclient==0.7.4 +nbconvert==7.4.0 +nbformat==5.8.0 +nest-asyncio==1.5.6 +notebook==6.5.4 +notebook_shim==0.2.3 +numpy==1.24.3 +openai==0.27.6 +opencv-python==4.7.0.72 +orjson==3.8.12 +packaging==23.1 +pandas==2.0.1 +pandocfilters==1.5.0 +parso==0.8.3 +pexpect==4.8.0 +pickleshare==0.7.5 +Pillow==9.5.0 +platformdirs==3.5.0 +prometheus-client==0.16.0 +prompt-toolkit==3.0.38 +psutil==5.9.5 +ptyprocess==0.7.0 +pure-eval==0.2.2 +pycparser==2.21 +pydantic==1.10.7 +pydub==0.25.1 +Pygments==2.15.1 +pyparsing==3.0.9 +pyrsistent==0.19.3 +python-dateutil==2.8.2 +python-dotenv==1.0.0 +python-json-logger==2.0.7 +python-multipart==0.0.6 +pytz==2023.3 +PyYAML==6.0 +pyzmq==25.0.2 +regex==2023.5.5 +requests==2.30.0 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +semantic-version==2.10.0 +Send2Trash==1.8.2 +six==1.16.0 +sniffio==1.3.0 +soupsieve==2.4.1 +stack-data==0.6.2 +starlette==0.26.1 +terminado==0.17.1 +tiktoken==0.3.3 +tinycss2==1.2.1 +tomli==2.0.1 +toolz==0.12.0 +tornado==6.3.1 +tqdm==4.65.0 +traitlets==5.9.0 +typing_extensions==4.5.0 +tzdata==2023.3 +uc-micro-py==1.0.2 +uri-template==1.2.0 +urllib3==2.0.2 +uvicorn==0.22.0 +uvloop==0.17.0 +watchfiles==0.19.0 +wcwidth==0.2.6 +webcolors==1.13 +webencodings==0.5.1 +websocket-client==1.5.1 +websockets==11.0.3 +y-py==0.5.9 +yarl==1.9.2 +ypy-websocket==0.8.2 \ No newline at end of file diff --git a/settings/admin.yml b/settings/admin.yml index 0494d9a..46ff6ed 100644 --- a/settings/admin.yml +++ b/settings/admin.yml @@ -1,9 +1,8 @@ -commander: - drone_interface: - wlan1 - net_interface: - wlan0 - drone_wpa_supp: - wpa_supp_djituad0_plus.conf - net_wpa_supp: - wpa_supp_uadis.conf +drone_interface: + wlan1 +net_interface: + wlan0 +drone_wpa_supp: + wpa_supp_djituad0_plus.conf +net_wpa_supp: + wpa_supp_uadis.conf