123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- import json
- import os
- import shutil
- import signal
- import subprocess
- import sys
- import tempfile
- import time
- import zipfile
- from pathlib import Path
- import click
- import requests
- from rich.console import Console
- from embedchain.telemetry.posthog import AnonymousTelemetry
- from embedchain.utils.cli import (deploy_fly, deploy_gradio_app,
- deploy_hf_spaces, deploy_modal,
- deploy_render, deploy_streamlit,
- get_pkg_path_from_name, setup_fly_io_app,
- setup_gradio_app, setup_hf_app,
- setup_modal_com_app, setup_render_com_app,
- setup_streamlit_io_app)
- console = Console()
- api_process = None
- ui_process = None
- anonymous_telemetry = AnonymousTelemetry()
- def signal_handler(sig, frame):
- """Signal handler to catch termination signals and kill server processes."""
- global api_process, ui_process
- console.print("\n🛑 [bold yellow]Stopping servers...[/bold yellow]")
- if api_process:
- api_process.terminate()
- console.print("🛑 [bold yellow]API server stopped.[/bold yellow]")
- if ui_process:
- ui_process.terminate()
- console.print("🛑 [bold yellow]UI server stopped.[/bold yellow]")
- sys.exit(0)
- @click.group()
- def cli():
- pass
- @cli.command()
- @click.argument("app_name")
- @click.option("--docker", is_flag=True, help="Use docker to create the app.")
- @click.pass_context
- def create_app(ctx, app_name, docker):
- if Path(app_name).exists():
- console.print(
- f"❌ [red]Directory '{app_name}' already exists. Try using a new directory name, or remove it.[/red]"
- )
- return
- os.makedirs(app_name)
- os.chdir(app_name)
- # Step 1: Download the zip file
- zip_url = "http://github.com/embedchain/ec-admin/archive/main.zip"
- console.print(f"Creating a new embedchain app in [green]{Path().resolve()}[/green]\n")
- try:
- response = requests.get(zip_url)
- response.raise_for_status()
- with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
- tmp_file.write(response.content)
- zip_file_path = tmp_file.name
- console.print("✅ [bold green]Fetched template successfully.[/bold green]")
- except requests.RequestException as e:
- console.print(f"❌ [bold red]Failed to download zip file: {e}[/bold red]")
- anonymous_telemetry.capture(event_name="ec_create_app", properties={"success": False})
- return
- # Step 2: Extract the zip file
- try:
- with zipfile.ZipFile(zip_file_path, "r") as zip_ref:
- # Get the name of the root directory inside the zip file
- root_dir = Path(zip_ref.namelist()[0])
- for member in zip_ref.infolist():
- # Build the path to extract the file to, skipping the root directory
- target_file = Path(member.filename).relative_to(root_dir)
- source_file = zip_ref.open(member, "r")
- if member.is_dir():
- # Create directory if it doesn't exist
- os.makedirs(target_file, exist_ok=True)
- else:
- with open(target_file, "wb") as file:
- # Write the file
- shutil.copyfileobj(source_file, file)
- console.print("✅ [bold green]Extracted zip file successfully.[/bold green]")
- anonymous_telemetry.capture(event_name="ec_create_app", properties={"success": True})
- except zipfile.BadZipFile:
- console.print("❌ [bold red]Error in extracting zip file. The file might be corrupted.[/bold red]")
- anonymous_telemetry.capture(event_name="ec_create_app", properties={"success": False})
- return
- if docker:
- subprocess.run(["docker-compose", "build"], check=True)
- else:
- ctx.invoke(install_reqs)
- @cli.command()
- def install_reqs():
- try:
- console.print("Installing python requirements...\n")
- time.sleep(2)
- os.chdir("api")
- subprocess.run(["pip", "install", "-r", "requirements.txt"], check=True)
- os.chdir("..")
- console.print("\n ✅ [bold green]Installed API requirements successfully.[/bold green]\n")
- except Exception as e:
- console.print(f"❌ [bold red]Failed to install API requirements: {e}[/bold red]")
- anonymous_telemetry.capture(event_name="ec_install_reqs", properties={"success": False})
- return
- try:
- os.chdir("ui")
- subprocess.run(["yarn"], check=True)
- console.print("\n✅ [bold green]Successfully installed frontend requirements.[/bold green]")
- anonymous_telemetry.capture(event_name="ec_install_reqs", properties={"success": True})
- except Exception as e:
- console.print(f"❌ [bold red]Failed to install frontend requirements. Error: {e}[/bold red]")
- anonymous_telemetry.capture(event_name="ec_install_reqs", properties={"success": False})
- @cli.command()
- @click.option("--docker", is_flag=True, help="Run inside docker.")
- def start(docker):
- if docker:
- subprocess.run(["docker-compose", "up"], check=True)
- return
- # Set up signal handling
- signal.signal(signal.SIGINT, signal_handler)
- signal.signal(signal.SIGTERM, signal_handler)
- # Step 1: Start the API server
- try:
- os.chdir("api")
- api_process = subprocess.Popen(["python", "-m", "main"], stdout=None, stderr=None)
- os.chdir("..")
- console.print("✅ [bold green]API server started successfully.[/bold green]")
- except Exception as e:
- console.print(f"❌ [bold red]Failed to start the API server: {e}[/bold red]")
- anonymous_telemetry.capture(event_name="ec_start", properties={"success": False})
- return
- # Sleep for 2 seconds to give the user time to read the message
- time.sleep(2)
- # Step 2: Install UI requirements and start the UI server
- try:
- os.chdir("ui")
- subprocess.run(["yarn"], check=True)
- ui_process = subprocess.Popen(["yarn", "dev"])
- console.print("✅ [bold green]UI server started successfully.[/bold green]")
- anonymous_telemetry.capture(event_name="ec_start", properties={"success": True})
- except Exception as e:
- console.print(f"❌ [bold red]Failed to start the UI server: {e}[/bold red]")
- anonymous_telemetry.capture(event_name="ec_start", properties={"success": False})
- # Keep the script running until it receives a kill signal
- try:
- api_process.wait()
- ui_process.wait()
- except KeyboardInterrupt:
- console.print("\n🛑 [bold yellow]Stopping server...[/bold yellow]")
- @cli.command()
- @click.option("--template", default="fly.io", help="The template to use.")
- @click.argument("extra_args", nargs=-1, type=click.UNPROCESSED)
- def create(template, extra_args):
- anonymous_telemetry.capture(event_name="ec_create", properties={"template_used": template})
- template_dir = template
- if "/" in template_dir:
- template_dir = template.split("/")[1]
- src_path = get_pkg_path_from_name(template_dir)
- shutil.copytree(src_path, os.getcwd(), dirs_exist_ok=True)
- console.print(f"✅ [bold green]Successfully created app from template '{template}'.[/bold green]")
- if template == "fly.io":
- setup_fly_io_app(extra_args)
- elif template == "modal.com":
- setup_modal_com_app(extra_args)
- elif template == "render.com":
- setup_render_com_app()
- elif template == "streamlit.io":
- setup_streamlit_io_app()
- elif template == "gradio.app":
- setup_gradio_app()
- elif template == "hf/gradio.app" or template == "hf/streamlit.io":
- setup_hf_app()
- else:
- raise ValueError(f"Unknown template '{template}'.")
- embedchain_config = {"provider": template}
- with open("embedchain.json", "w") as file:
- json.dump(embedchain_config, file, indent=4)
- console.print(
- f"🎉 [green]All done! Successfully created `embedchain.json` with '{template}' as provider.[/green]"
- )
- def run_dev_fly_io(debug, host, port):
- uvicorn_command = ["uvicorn", "app:app"]
- if debug:
- uvicorn_command.append("--reload")
- uvicorn_command.extend(["--host", host, "--port", str(port)])
- try:
- console.print(f"🚀 [bold cyan]Running FastAPI app with command: {' '.join(uvicorn_command)}[/bold cyan]")
- subprocess.run(uvicorn_command, check=True)
- except subprocess.CalledProcessError as e:
- console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
- except KeyboardInterrupt:
- console.print("\n🛑 [bold yellow]FastAPI server stopped[/bold yellow]")
- def run_dev_modal_com():
- modal_run_cmd = ["modal", "serve", "app"]
- try:
- console.print(f"🚀 [bold cyan]Running FastAPI app with command: {' '.join(modal_run_cmd)}[/bold cyan]")
- subprocess.run(modal_run_cmd, check=True)
- except subprocess.CalledProcessError as e:
- console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
- except KeyboardInterrupt:
- console.print("\n🛑 [bold yellow]FastAPI server stopped[/bold yellow]")
- def run_dev_streamlit_io():
- streamlit_run_cmd = ["streamlit", "run", "app.py"]
- try:
- console.print(f"🚀 [bold cyan]Running Streamlit app with command: {' '.join(streamlit_run_cmd)}[/bold cyan]")
- subprocess.run(streamlit_run_cmd, check=True)
- except subprocess.CalledProcessError as e:
- console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
- except KeyboardInterrupt:
- console.print("\n🛑 [bold yellow]Streamlit server stopped[/bold yellow]")
- def run_dev_render_com(debug, host, port):
- uvicorn_command = ["uvicorn", "app:app"]
- if debug:
- uvicorn_command.append("--reload")
- uvicorn_command.extend(["--host", host, "--port", str(port)])
- try:
- console.print(f"🚀 [bold cyan]Running FastAPI app with command: {' '.join(uvicorn_command)}[/bold cyan]")
- subprocess.run(uvicorn_command, check=True)
- except subprocess.CalledProcessError as e:
- console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
- except KeyboardInterrupt:
- console.print("\n🛑 [bold yellow]FastAPI server stopped[/bold yellow]")
- def run_dev_gradio():
- gradio_run_cmd = ["gradio", "app.py"]
- try:
- console.print(f"🚀 [bold cyan]Running Gradio app with command: {' '.join(gradio_run_cmd)}[/bold cyan]")
- subprocess.run(gradio_run_cmd, check=True)
- except subprocess.CalledProcessError as e:
- console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
- except KeyboardInterrupt:
- console.print("\n🛑 [bold yellow]Gradio server stopped[/bold yellow]")
- @cli.command()
- @click.option("--debug", is_flag=True, help="Enable or disable debug mode.")
- @click.option("--host", default="127.0.0.1", help="The host address to run the FastAPI app on.")
- @click.option("--port", default=8000, help="The port to run the FastAPI app on.")
- def dev(debug, host, port):
- template = ""
- with open("embedchain.json", "r") as file:
- embedchain_config = json.load(file)
- template = embedchain_config["provider"]
- anonymous_telemetry.capture(event_name="ec_dev", properties={"template_used": template})
- if template == "fly.io":
- run_dev_fly_io(debug, host, port)
- elif template == "modal.com":
- run_dev_modal_com()
- elif template == "render.com":
- run_dev_render_com(debug, host, port)
- elif template == "streamlit.io" or template == "hf/streamlit.io":
- run_dev_streamlit_io()
- elif template == "gradio.app" or template == "hf/gradio.app":
- run_dev_gradio()
- else:
- raise ValueError(f"Unknown template '{template}'.")
- @cli.command()
- def deploy():
- # Check for platform-specific files
- template = ""
- ec_app_name = ""
- with open("embedchain.json", "r") as file:
- embedchain_config = json.load(file)
- ec_app_name = embedchain_config["name"] if "name" in embedchain_config else None
- template = embedchain_config["provider"]
- anonymous_telemetry.capture(event_name="ec_deploy", properties={"template_used": template})
- if template == "fly.io":
- deploy_fly()
- elif template == "modal.com":
- deploy_modal()
- elif template == "render.com":
- deploy_render()
- elif template == "streamlit.io":
- deploy_streamlit()
- elif template == "gradio.app":
- deploy_gradio_app()
- elif template.startswith("hf/"):
- deploy_hf_spaces(ec_app_name)
- else:
- console.print("❌ [bold red]No recognized deployment platform found.[/bold red]")
|