[Feature] Add render.com deployment template (#1033)

Co-authored-by: Deven Patel <iamdevenpatel@gmail.com>
This commit is contained in:
Sidharth Mohanty
2023-12-20 22:38:23 +05:30
committed by GitHub
parent 7ee2d0653b
commit 737837ae0b
9 changed files with 233 additions and 61 deletions

View File

@@ -0,0 +1,93 @@
---
title: 'Render.com'
description: 'Deploy your RAG application to render.com platform'
---
Embedchain has a nice and simple abstraction on top of the [render.com](https://render.com/) tools to let developers deploy RAG application to render.com platform seamlessly.
Follow the instructions given below to deploy your first application quickly:
## Step-1: Install `render` command line
<CodeGroup>
```bash OSX
brew tap render-oss/render
brew install render
```
```bash Linux
# Make sure you have deno installed -> https://docs.render.com/docs/cli#from-source-unsupported-operating-systems
git clone https://github.com/render-oss/render-cli
cd render-cli
make deps
deno task run
deno compile
```
```bash Windows
choco install rendercli
```
</CodeGroup>
In case you run into issues, refer to official [render.com docs](https://docs.render.com/docs/cli).
## Step-2 Create RAG application:
We provide a command line utility called `ec` in embedchain that inherits the template for `render.com` platform and help you deploy the app. Follow the instructions to create a render.com app using the template provided:
```bash Create application
pip install embedchain
mkdir my-rag-app
ec create --template=render.com
```
This `create` command will open a browser window and ask you to login to your render.com account and will generate a directory structure like this:
```bash
├── app.py
├── .env
├── render.yaml
├── embedchain.json
└── requirements.txt
```
Feel free to edit the files as required.
- `app.py`: Contains API app code
- `.env`: Contains environment variables for production
- `render.yaml`: Contains render.com specific configuration for deployment (configure this according to your needs, follow [this](https://docs.render.com/docs/blueprint-spec) for more info)
- `embedchain.json`: Contains embedchain specific configuration for deployment (you don't need to configure this)
- `requirements.txt`: Contains python dependencies for your application
## Step-3: Test app locally
You can run the app locally by simply doing:
```bash Run locally
pip install -r requirements.txt
ec dev
```
## Step-4: Deploy to render.com
Before deploying to render.com, you only have to set up one thing.
In the render.yaml file, make sure to modify the repo key by inserting the URL of your Git repository where your application will be hosted. You can create a repository from [GitHub](https://github.com) or [GitLab](https://gitlab.com/users/sign_in).
After that, you're ready to deploy on render.com.
```bash Deploy app
ec deploy
```
When you run this, it should open up your render dashboard and you can see the app being deployed. You can find your hosted link over there only.
You can also check the logs, monitor app status etc on their dashboard by running command `render dashboard`.
<img src="/images/fly_io.png" />
## Seeking help?
If you run into issues with deployment, please feel free to reach out to us via any of the following methods:
<Snippet file="get-help.mdx" />

View File

@@ -8,6 +8,7 @@ After successfully setting up and testing your RAG app locally, the next step is
<CardGroup cols={4}> <CardGroup cols={4}>
<Card title="Fly.io" href="/deployment/fly_io"></Card> <Card title="Fly.io" href="/deployment/fly_io"></Card>
<Card title="Modal.com" href="/deployment/modal_com"></Card> <Card title="Modal.com" href="/deployment/modal_com"></Card>
<Card title="Render.com" href="/deployment/render_com"></Card>
<Card title="Embedchain Platform" href="#option-1-deploy-on-embedchain-platform"></Card> <Card title="Embedchain Platform" href="#option-1-deploy-on-embedchain-platform"></Card>
<Card title="Self-hosting" href="#option-2-self-hosting"></Card> <Card title="Self-hosting" href="#option-2-self-hosting"></Card>
</CardGroup> </CardGroup>

View File

@@ -88,7 +88,8 @@
"pages": [ "pages": [
"get-started/deployment", "get-started/deployment",
"deployment/fly_io", "deployment/fly_io",
"deployment/modal_com" "deployment/modal_com",
"deployment/render_com"
] ]
}, },
{ {

View File

@@ -43,6 +43,7 @@ def setup_fly_io_app(extra_args):
fly_launch_command = ["fly", "launch", "--region", "sjc", "--no-deploy"] + list(extra_args) fly_launch_command = ["fly", "launch", "--region", "sjc", "--no-deploy"] + list(extra_args)
try: try:
console.print(f"🚀 [bold cyan]Running: {' '.join(fly_launch_command)}[/bold cyan]") console.print(f"🚀 [bold cyan]Running: {' '.join(fly_launch_command)}[/bold cyan]")
shutil.move(".env.example", ".env")
subprocess.run(fly_launch_command, check=True) subprocess.run(fly_launch_command, check=True)
console.print("✅ [bold green]'fly launch' executed successfully.[/bold green]") console.print("✅ [bold green]'fly launch' executed successfully.[/bold green]")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
@@ -60,12 +61,41 @@ def setup_modal_com_app(extra_args):
"""✅ [bold green]Modal setup already done. You can now install the dependencies by doing \n """✅ [bold green]Modal setup already done. You can now install the dependencies by doing \n
`pip install -r requirements.txt`[/bold green]""" `pip install -r requirements.txt`[/bold green]"""
) )
return else:
modal_setup_cmd = ["modal", "setup"] + list(extra_args) modal_setup_cmd = ["modal", "setup"] + list(extra_args)
console.print(f"🚀 [bold cyan]Running: {' '.join(modal_setup_cmd)}[/bold cyan]") console.print(f"🚀 [bold cyan]Running: {' '.join(modal_setup_cmd)}[/bold cyan]")
subprocess.run(modal_setup_cmd, check=True) subprocess.run(modal_setup_cmd, check=True)
shutil.move(".env.example", ".env") shutil.move(".env.example", ".env")
console.print("Great! Now you can install the dependencies by doing `pip install -r requirements.txt`") console.print(
"""Great! Now you can install the dependencies by doing: \n
`pip install -r requirements.txt`\n
\n
To run your app locally:\n
`ec dev`
"""
)
def setup_render_com_app():
render_setup_file = os.path.join(os.path.expanduser("~"), ".render/config.yaml")
if os.path.exists(render_setup_file):
console.print(
"""✅ [bold green]Render setup already done. You can now install the dependencies by doing \n
`pip install -r requirements.txt`[/bold green]"""
)
else:
render_setup_cmd = ["render", "config", "init"]
console.print(f"🚀 [bold cyan]Running: {' '.join(render_setup_cmd)}[/bold cyan]")
subprocess.run(render_setup_cmd, check=True)
shutil.move(".env.example", ".env")
console.print(
"""Great! Now you can install the dependencies by doing: \n
`pip install -r requirements.txt`\n
\n
To run your app locally:\n
`ec dev`
"""
)
@cli.command() @cli.command()
@@ -75,15 +105,14 @@ def create(template, extra_args):
anonymous_telemetry.capture(event_name="ec_create", properties={"template_used": template}) anonymous_telemetry.capture(event_name="ec_create", properties={"template_used": template})
src_path = get_pkg_path_from_name(template) src_path = get_pkg_path_from_name(template)
shutil.copytree(src_path, os.getcwd(), dirs_exist_ok=True) shutil.copytree(src_path, os.getcwd(), dirs_exist_ok=True)
env_sample_path = os.path.join(src_path, ".env.example")
if os.path.exists(env_sample_path):
shutil.copy(env_sample_path, os.path.join(os.getcwd(), ".env"))
console.print(f"✅ [bold green]Successfully created app from template '{template}'.[/bold green]") console.print(f"✅ [bold green]Successfully created app from template '{template}'.[/bold green]")
if template == "fly.io": if template == "fly.io":
setup_fly_io_app(extra_args) setup_fly_io_app(extra_args)
elif template == "modal.com": elif template == "modal.com":
setup_modal_com_app(extra_args) setup_modal_com_app(extra_args)
elif template == "render.com":
setup_render_com_app()
else: else:
raise ValueError(f"Unknown template '{template}'.") raise ValueError(f"Unknown template '{template}'.")
@@ -123,6 +152,23 @@ def run_dev_modal_com():
console.print("\n🛑 [bold yellow]FastAPI server stopped[/bold yellow]") console.print("\n🛑 [bold yellow]FastAPI 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]")
@cli.command() @cli.command()
@click.option("--debug", is_flag=True, help="Enable or disable debug mode.") @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("--host", default="127.0.0.1", help="The host address to run the FastAPI app on.")
@@ -138,6 +184,8 @@ def dev(debug, host, port):
run_dev_fly_io(debug, host, port) run_dev_fly_io(debug, host, port)
elif template == "modal.com": elif template == "modal.com":
run_dev_modal_com() run_dev_modal_com()
elif template == "render.com":
run_dev_render_com(debug, host, port)
else: else:
raise ValueError(f"Unknown template '{template}'.") raise ValueError(f"Unknown template '{template}'.")
@@ -212,6 +260,21 @@ def deploy_modal():
) )
def deploy_render():
render_deploy_cmd = ["render", "blueprint", "launch"]
try:
console.print(f"🚀 [bold cyan]Running: {' '.join(render_deploy_cmd)}[/bold cyan]")
subprocess.run(render_deploy_cmd, check=True)
console.print("✅ [bold green]'render blueprint launch' executed successfully.[/bold green]")
except subprocess.CalledProcessError as e:
console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
except FileNotFoundError:
console.print(
"❌ [bold red]'render' command not found. Please ensure Render CLI is installed and in your PATH.[/bold red]"
)
@cli.command() @cli.command()
def deploy(): def deploy():
# Check for platform-specific files # Check for platform-specific files
@@ -225,5 +288,7 @@ def deploy():
deploy_fly() deploy_fly()
elif template == "modal.com": elif template == "modal.com":
deploy_modal() deploy_modal()
elif template == "render.com":
deploy_render()
else: else:
console.print("❌ [bold red]No recognized deployment platform found.[/bold red]") console.print("❌ [bold red]No recognized deployment platform found.[/bold red]")

View File

@@ -0,0 +1 @@
OPENAI_API_KEY=sk-xxx

View File

@@ -0,0 +1 @@
.env

View File

@@ -1,58 +1,53 @@
import logging from fastapi import FastAPI, responses
import os from pydantic import BaseModel
from flask import Flask, jsonify, request from embedchain import Pipeline
from embedchain import Pipeline as App app = FastAPI(title="Embedchain FastAPI App")
embedchain_app = Pipeline()
app = Flask(__name__)
os.environ["OPENAI_API_KEY"] = "sk-xxx"
@app.route("/add", methods=["POST"]) class SourceModel(BaseModel):
def add(): source: str
data = request.get_json()
data_type = data.get("data_type")
url_or_text = data.get("url_or_text")
if data_type and url_or_text:
try:
App().add(url_or_text, data_type=data_type)
return jsonify({"data": f"Added {data_type}: {url_or_text}"}), 200
except Exception:
logging.exception(f"Failed to add {data_type=}: {url_or_text=}")
return jsonify({"error": f"Failed to add {data_type}: {url_or_text}"}), 500
return jsonify({"error": "Invalid request. Please provide 'data_type' and 'url_or_text' in JSON format."}), 400
@app.route("/query", methods=["POST"]) class QuestionModel(BaseModel):
def query(): question: str
data = request.get_json()
question = data.get("question")
if question:
try:
response = App().query(question)
return jsonify({"data": response}), 200
except Exception:
logging.exception(f"Failed to query {question=}")
return jsonify({"error": "An error occurred. Please try again!"}), 500
return jsonify({"error": "Invalid request. Please provide 'question' in JSON format."}), 400
@app.route("/chat", methods=["POST"]) @app.post("/add")
def chat(): async def add_source(source_model: SourceModel):
data = request.get_json() """
question = data.get("question") Adds a new source to the EmbedChain app.
if question: Expects a JSON with a "source" key.
try: """
response = App().chat(question) source = source_model.source
return jsonify({"data": response}), 200 embedchain_app.add(source)
except Exception: return {"message": f"Source '{source}' added successfully."}
logging.exception(f"Failed to chat {question=}")
return jsonify({"error": "An error occurred. Please try again!"}), 500
return jsonify({"error": "Invalid request. Please provide 'question' in JSON format."}), 400
@app.route("/api/python") @app.post("/query")
def hello_world(): async def handle_query(question_model: QuestionModel):
return "<p>Hello, World!</p>" """
Handles a query to the EmbedChain app.
Expects a JSON with a "question" key.
"""
question = question_model.question
answer = embedchain_app.query(question)
return {"answer": answer}
@app.post("/chat")
async def handle_chat(question_model: QuestionModel):
"""
Handles a chat request to the EmbedChain app.
Expects a JSON with a "question" key.
"""
question = question_model.question
response = embedchain_app.chat(question)
return {"response": response}
@app.get("/")
async def root():
return responses.RedirectResponse(url="/docs")

View File

@@ -0,0 +1,16 @@
services:
- type: web
name: ec-render-app
runtime: python
repo: https://github.com/<your-username>/<repo-name>
scaling:
minInstances: 1
maxInstances: 3
targetMemoryPercent: 60 # optional if targetCPUPercent is set
targetCPUPercent: 60 # optional if targetMemory is set
buildCommand: pip install -r requirements.txt
startCommand: uvicorn app:app --host 0.0.0.0
envVars:
- key: OPENAI_API_KEY
value: sk-xxx
autoDeploy: false # optional

View File

@@ -1,5 +1,4 @@
numpy==1.24.3 fastapi==0.104.0
Flask==2.2.2 uvicorn==0.23.2
Werkzeug==2.2.2
gunicorn
embedchain embedchain
beautifulsoup4