[Feature] Add render.com deployment template (#1033)
Co-authored-by: Deven Patel <iamdevenpatel@gmail.com>
This commit is contained in:
93
docs/deployment/render_com.mdx
Normal file
93
docs/deployment/render_com.mdx
Normal 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" />
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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]")
|
||||||
|
|||||||
1
embedchain/deployment/render.com/.env.example
Normal file
1
embedchain/deployment/render.com/.env.example
Normal file
@@ -0,0 +1 @@
|
|||||||
|
OPENAI_API_KEY=sk-xxx
|
||||||
1
embedchain/deployment/render.com/.gitignore
vendored
Normal file
1
embedchain/deployment/render.com/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.env
|
||||||
@@ -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")
|
||||||
|
|||||||
16
embedchain/deployment/render.com/render.yaml
Normal file
16
embedchain/deployment/render.com/render.yaml
Normal 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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user