diff --git a/knowledgehub/cli.py b/knowledgehub/cli.py index 8272307..fc69858 100644 --- a/knowledgehub/cli.py +++ b/knowledgehub/cli.py @@ -67,7 +67,7 @@ def export(export_path, output): @click.option( "--appname", required=False, - default="The share app subdomain. Requires --share and --username", + help="The share app subdomain. Requires --share and --username", ) @click.option( "--port", @@ -121,39 +121,13 @@ def run(run_path, share, username, password, appname, port): "Username must be provided to enable authentication for sharing" ) if appname: - command = [ - "frpc", - "http", - "-l", - str(port), - "-i", - "127.0.0.1", - "--uc", - "--sd", - str(appname), - "-n", - str(appname + username), - "--server_addr", - "35.92.162.75:7000", - "--token", - "Wz807/DyC;#t;#/", - "--disable_log_color", - ] - import atexit - import subprocess + from kotaemon.contribs.promptui.tunnel import Tunnel - proc = subprocess.Popen( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + tunnel = Tunnel( + appname=str(appname), username=str(username), local_port=port ) - - def kill_proc(): - if proc is not None: - print(f"Killing tunnel: https://{appname}.promptui.dm.cinnamon.is") - proc.terminate() - - atexit.register(kill_proc) - - print(f"App is shared at https://{appname}.promptui.dm.cinnamon.is") + url = tunnel.run() + print(f"App is shared at {url}") else: params["share"] = True print("App is shared at Gradio") diff --git a/knowledgehub/contribs/promptui/.gitignore b/knowledgehub/contribs/promptui/.gitignore new file mode 100644 index 0000000..632ca74 --- /dev/null +++ b/knowledgehub/contribs/promptui/.gitignore @@ -0,0 +1 @@ +/frpc_* diff --git a/knowledgehub/contribs/promptui/tunnel.py b/knowledgehub/contribs/promptui/tunnel.py new file mode 100644 index 0000000..897a438 --- /dev/null +++ b/knowledgehub/contribs/promptui/tunnel.py @@ -0,0 +1,107 @@ +import atexit +import logging +import os +import platform +import stat +import subprocess +from pathlib import Path + +import requests + +VERSION = "1.0" + +machine = platform.machine() +if machine == "x86_64": + machine = "amd64" + +BINARY_REMOTE_NAME = f"frpc_{platform.system().lower()}_{machine.lower()}" +EXTENSION = ".exe" if os.name == "nt" else "" +BINARY_URL = ( + "some-endpoint.com" + f"/kotaemon/tunneling/{VERSION}/{BINARY_REMOTE_NAME}{EXTENSION}" +) + +BINARY_FILENAME = f"{BINARY_REMOTE_NAME}_v{VERSION}" +BINARY_FOLDER = Path(__file__).parent +BINARY_PATH = f"{BINARY_FOLDER / BINARY_FILENAME}" + + +logger = logging.getLogger(__name__) + + +class Tunnel: + def __init__(self, appname, username, local_port): + self.proc = None + self.url = None + self.appname = appname + self.username = username + self.local_port = local_port + + @staticmethod + def download_binary(): + if not Path(BINARY_PATH).exists(): + print("First time setting tunneling...") + resp = requests.get(BINARY_URL) + + if resp.status_code == 404: + raise OSError( + f"Cannot set up a share link as this platform is incompatible. " + "Please create a GitHub issue with information about your " + f"platform: {platform.uname()}" + ) + + if resp.status_code == 403: + raise OSError( + "You do not have permission to setup the tunneling. Please " + "make sure that you are within Cinnamon VPN or within other " + "approved IPs. If this is new server, please contact @channel " + "at #llm-productization to add your IP address" + ) + + resp.raise_for_status() + + # Save file data to local copy + with open(BINARY_PATH, "wb") as file: + file.write(resp.content) + st = os.stat(BINARY_PATH) + os.chmod(BINARY_PATH, st.st_mode | stat.S_IEXEC) + + def run(self) -> str: + """Setting up tunneling""" + if platform.system().lower() == "windows": + logger.warning("Tunneling is not fully supported on Windows.") + + self.download_binary() + self.url = self._start_tunnel(BINARY_PATH) + return self.url + + def kill(self): + if self.proc is not None: + print(f"Killing tunnel 127.0.0.1:{self.local_port} <> {self.url}") + self.proc.terminate() + self.proc = None + + def _start_tunnel(self, binary: str) -> str: + command = [ + binary, + "http", + "-l", + str(self.local_port), + "-i", + "127.0.0.1", + "--uc", + "--sd", + str(self.appname), + "-n", + str(self.appname + self.username), + "--server_addr", + "44.229.38.9:7000", + "--token", + "Wz807/DyC;#t;#/", + "--disable_log_color", + ] + self.proc = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + atexit.register(self.kill) + return f"https://{self.appname}.promptui.dm.cinnamon.is" diff --git a/knowledgehub/contribs/promptui/ui/pipeline.py b/knowledgehub/contribs/promptui/ui/pipeline.py index 08d90bf..725893d 100644 --- a/knowledgehub/contribs/promptui/ui/pipeline.py +++ b/knowledgehub/contribs/promptui/ui/pipeline.py @@ -106,12 +106,16 @@ def construct_pipeline_ui( export_btn.click(func_export, inputs=None, outputs=exported_file) with gr.Row(): with gr.Column(): - with temp("Params"): - for component in params: - component.render() - with temp("Inputs"): - for component in inputs: - component.render() + if params: + with temp("Params"): + for component in params: + component.render() + if inputs: + with temp("Inputs"): + for component in inputs: + component.render() + if not params and not inputs: + gr.Text("No params or inputs") with gr.Column(): with temp("Outputs"): for component in outputs: