Add rest-api example (#889)
This commit is contained in:
4
examples/rest-api/.dockerignore
Normal file
4
examples/rest-api/.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
||||
.env
|
||||
app.db
|
||||
configs/**.yaml
|
||||
db
|
||||
4
examples/rest-api/.gitignore
vendored
Normal file
4
examples/rest-api/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.env
|
||||
app.db
|
||||
configs/**.yaml
|
||||
db
|
||||
15
examples/rest-api/Dockerfile
Normal file
15
examples/rest-api/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt /app/
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . /app
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
ENV NAME embedchain
|
||||
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
21
examples/rest-api/README.md
Normal file
21
examples/rest-api/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## Single command to rule them all,
|
||||
|
||||
```bash
|
||||
docker run -d --name embedchain -p 8000:8000 embedchain/app:rest-api-latest
|
||||
```
|
||||
|
||||
### To run the app locally,
|
||||
|
||||
```bash
|
||||
# will help reload on changes
|
||||
DEVELOPMENT=True && python -m main
|
||||
```
|
||||
|
||||
Using docker (locally),
|
||||
|
||||
```bash
|
||||
docker build -t embedchain/app:rest-api-latest .
|
||||
docker run -d --name embedchain -p 8000:8000 embedchain/app:rest-api-latest
|
||||
docker image push embedchain/app:rest-api-latest
|
||||
```
|
||||
|
||||
0
examples/rest-api/__init__.py
Normal file
0
examples/rest-api/__init__.py
Normal file
5
examples/rest-api/bruno/ec-rest-api/bruno.json
Normal file
5
examples/rest-api/bruno/ec-rest-api/bruno.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "ec-rest-api",
|
||||
"type": "collection"
|
||||
}
|
||||
18
examples/rest-api/bruno/ec-rest-api/default_add.bru
Normal file
18
examples/rest-api/bruno/ec-rest-api/default_add.bru
Normal file
@@ -0,0 +1,18 @@
|
||||
meta {
|
||||
name: default_add
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
post {
|
||||
url: http://localhost:8000/add
|
||||
body: json
|
||||
auth: none
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"source": "source_url",
|
||||
"data_type": "data_type"
|
||||
}
|
||||
}
|
||||
17
examples/rest-api/bruno/ec-rest-api/default_chat.bru
Normal file
17
examples/rest-api/bruno/ec-rest-api/default_chat.bru
Normal file
@@ -0,0 +1,17 @@
|
||||
meta {
|
||||
name: default_chat
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
post {
|
||||
url: http://localhost:8000/chat
|
||||
body: json
|
||||
auth: none
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"message": "message"
|
||||
}
|
||||
}
|
||||
17
examples/rest-api/bruno/ec-rest-api/default_query.bru
Normal file
17
examples/rest-api/bruno/ec-rest-api/default_query.bru
Normal file
@@ -0,0 +1,17 @@
|
||||
meta {
|
||||
name: default_query
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
post {
|
||||
url: http://localhost:8000/query
|
||||
body: json
|
||||
auth: none
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"query": "Who is Elon Musk?"
|
||||
}
|
||||
}
|
||||
11
examples/rest-api/bruno/ec-rest-api/ping.bru
Normal file
11
examples/rest-api/bruno/ec-rest-api/ping.bru
Normal file
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: ping
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: http://localhost:8000/ping
|
||||
body: json
|
||||
auth: none
|
||||
}
|
||||
3
examples/rest-api/configs/README.md
Normal file
3
examples/rest-api/configs/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Config directory
|
||||
|
||||
Here, all the YAML files will get stored.
|
||||
11
examples/rest-api/database.py
Normal file
11
examples/rest-api/database.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///./app.db"
|
||||
|
||||
engine = create_engine(SQLALCHEMY_DATABASE_URI, connect_args={"check_same_thread": False})
|
||||
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
Base = declarative_base()
|
||||
17
examples/rest-api/default.yaml
Normal file
17
examples/rest-api/default.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
app:
|
||||
config:
|
||||
id: 'default'
|
||||
|
||||
llm:
|
||||
provider: gpt4all
|
||||
config:
|
||||
model: 'orca-mini-3b.ggmlv3.q4_0.bin'
|
||||
temperature: 0.5
|
||||
max_tokens: 1000
|
||||
top_p: 1
|
||||
stream: false
|
||||
|
||||
embedder:
|
||||
provider: gpt4all
|
||||
config:
|
||||
model: 'all-MiniLM-L6-v2'
|
||||
321
examples/rest-api/main.py
Normal file
321
examples/rest-api/main.py
Normal file
@@ -0,0 +1,321 @@
|
||||
import os
|
||||
import yaml
|
||||
from fastapi import FastAPI, UploadFile, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from embedchain import Pipeline as App
|
||||
from embedchain.client import Client
|
||||
from models import (
|
||||
QueryApp,
|
||||
SourceApp,
|
||||
DefaultResponse,
|
||||
DeployAppRequest,
|
||||
)
|
||||
from database import Base, engine, SessionLocal
|
||||
from services import get_app, save_app, get_apps, remove_app
|
||||
from utils import generate_error_message_for_api_keys
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title="Embedchain REST API",
|
||||
description="This is the REST API for Embedchain.",
|
||||
version="0.0.1",
|
||||
license_info={
|
||||
"name": "Apache 2.0",
|
||||
"url": "https://github.com/embedchain/embedchain/blob/main/LICENSE",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/ping", tags=["Utility"])
|
||||
def check_status():
|
||||
"""
|
||||
Endpoint to check the status of the API.
|
||||
"""
|
||||
return {"ping": "pong"}
|
||||
|
||||
|
||||
@app.get("/apps", tags=["Apps"])
|
||||
async def get_all_apps(db: Session = Depends(get_db)):
|
||||
"""
|
||||
Get all apps.
|
||||
"""
|
||||
apps = get_apps(db)
|
||||
return {"results": apps}
|
||||
|
||||
|
||||
@app.post("/create", tags=["Apps"], response_model=DefaultResponse)
|
||||
async def create_app_using_default_config(app_id: str, config: UploadFile = None, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Create a new app using App ID.
|
||||
If you don't provide a config file, Embedchain will use the default config file\n
|
||||
which uses opensource GPT4ALL model.\n
|
||||
app_id: The ID of the app.\n
|
||||
config: The YAML config file to create an App.\n
|
||||
"""
|
||||
try:
|
||||
if app_id is None:
|
||||
raise HTTPException(detail="App ID not provided.", status_code=400)
|
||||
|
||||
if get_app(db, app_id) is not None:
|
||||
raise HTTPException(detail=f"App with id '{app_id}' already exists.", status_code=400)
|
||||
|
||||
yaml_path = "default.yaml"
|
||||
if config is not None:
|
||||
contents = await config.read()
|
||||
try:
|
||||
yaml.safe_load(contents)
|
||||
# TODO: validate the config yaml file here
|
||||
yaml_path = f"configs/{app_id}.yaml"
|
||||
with open(yaml_path, "w") as file:
|
||||
file.write(str(contents, "utf-8"))
|
||||
except yaml.YAMLError as exc:
|
||||
raise HTTPException(detail=f"Error parsing YAML: {exc}", status_code=400)
|
||||
|
||||
save_app(db, app_id, yaml_path)
|
||||
|
||||
return DefaultResponse(response=f"App created successfully. App ID: {app_id}")
|
||||
except Exception as e:
|
||||
raise HTTPException(detail=f"Error creating app: {e}", status_code=400)
|
||||
|
||||
|
||||
@app.get(
|
||||
"/{app_id}/data",
|
||||
tags=["Apps"],
|
||||
)
|
||||
async def get_datasources_associated_with_app_id(app_id: str, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Get all datasources for an app.\n
|
||||
app_id: The ID of the app. Use "default" for the default app.\n
|
||||
"""
|
||||
try:
|
||||
if app_id is None:
|
||||
raise HTTPException(
|
||||
detail="App ID not provided. If you want to use the default app, use 'default' as the app_id.",
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
db_app = get_app(db, app_id)
|
||||
|
||||
if db_app is None:
|
||||
raise HTTPException(detail=f"App with id {app_id} does not exist, please create it first.", status_code=400)
|
||||
|
||||
app = App.from_config(yaml_path=db_app.config)
|
||||
|
||||
response = app.get_data_sources()
|
||||
return {"results": response}
|
||||
except ValueError as ve:
|
||||
if "OPENAI_API_KEY" in str(ve) or "OPENAI_ORGANIZATION" in str(ve):
|
||||
raise HTTPException(
|
||||
detail=generate_error_message_for_api_keys(ve),
|
||||
status_code=400,
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(detail=f"Error occurred: {e}", status_code=400)
|
||||
|
||||
|
||||
@app.post(
|
||||
"/{app_id}/add",
|
||||
tags=["Apps"],
|
||||
response_model=DefaultResponse,
|
||||
)
|
||||
async def add_datasource_to_an_app(body: SourceApp, app_id: str, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Add a source to an existing app.\n
|
||||
app_id: The ID of the app. Use "default" for the default app.\n
|
||||
source: The source to add.\n
|
||||
data_type: The data type of the source. Remove it if you want Embedchain to detect it automatically.\n
|
||||
"""
|
||||
try:
|
||||
if app_id is None:
|
||||
raise HTTPException(
|
||||
detail="App ID not provided. If you want to use the default app, use 'default' as the app_id.",
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
db_app = get_app(db, app_id)
|
||||
|
||||
if db_app is None:
|
||||
raise HTTPException(detail=f"App with id {app_id} does not exist, please create it first.", status_code=400)
|
||||
|
||||
app = App.from_config(yaml_path=db_app.config)
|
||||
|
||||
response = app.add(source=body.source, data_type=body.data_type)
|
||||
return DefaultResponse(response=response)
|
||||
except ValueError as ve:
|
||||
if "OPENAI_API_KEY" in str(ve) or "OPENAI_ORGANIZATION" in str(ve):
|
||||
raise HTTPException(
|
||||
detail=generate_error_message_for_api_keys(ve),
|
||||
status_code=400,
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(detail=f"Error occurred: {e}", status_code=400)
|
||||
|
||||
|
||||
@app.post(
|
||||
"/{app_id}/query",
|
||||
tags=["Apps"],
|
||||
response_model=DefaultResponse,
|
||||
)
|
||||
async def query_an_app(body: QueryApp, app_id: str, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Query an existing app.\n
|
||||
app_id: The ID of the app. Use "default" for the default app.\n
|
||||
query: The query that you want to ask the App.\n
|
||||
"""
|
||||
try:
|
||||
if app_id is None:
|
||||
raise HTTPException(
|
||||
detail="App ID not provided. If you want to use the default app, use 'default' as the app_id.",
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
db_app = get_app(db, app_id)
|
||||
|
||||
if db_app is None:
|
||||
raise HTTPException(detail=f"App with id {app_id} does not exist, please create it first.", status_code=400)
|
||||
|
||||
app = App.from_config(yaml_path=db_app.config)
|
||||
|
||||
response = app.query(body.query)
|
||||
return DefaultResponse(response=response)
|
||||
except ValueError as ve:
|
||||
if "OPENAI_API_KEY" in str(ve) or "OPENAI_ORGANIZATION" in str(ve):
|
||||
raise HTTPException(
|
||||
detail=generate_error_message_for_api_keys(ve),
|
||||
status_code=400,
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(detail=f"Error occurred: {e}", status_code=400)
|
||||
|
||||
|
||||
# FIXME: The chat implementation of Embedchain needs to be modified to work with the REST API.
|
||||
# @app.post(
|
||||
# "/{app_id}/chat",
|
||||
# tags=["Apps"],
|
||||
# response_model=DefaultResponse,
|
||||
# )
|
||||
# async def chat_with_an_app(body: MessageApp, app_id: str, db: Session = Depends(get_db)):
|
||||
# """
|
||||
# Query an existing app.\n
|
||||
# app_id: The ID of the app. Use "default" for the default app.\n
|
||||
# message: The message that you want to send to the App.\n
|
||||
# """
|
||||
# try:
|
||||
# if app_id is None:
|
||||
# raise HTTPException(
|
||||
# detail="App ID not provided. If you want to use the default app, use 'default' as the app_id.",
|
||||
# status_code=400,
|
||||
# )
|
||||
|
||||
# db_app = get_app(db, app_id)
|
||||
|
||||
# if db_app is None:
|
||||
# raise HTTPException(
|
||||
# detail=f"App with id {app_id} does not exist, please create it first.",
|
||||
# status_code=400
|
||||
# )
|
||||
|
||||
# app = App.from_config(yaml_path=db_app.config)
|
||||
|
||||
# response = app.chat(body.message)
|
||||
# return DefaultResponse(response=response)
|
||||
# except ValueError as ve:
|
||||
# if "OPENAI_API_KEY" in str(ve) or "OPENAI_ORGANIZATION" in str(ve):
|
||||
# raise HTTPException(
|
||||
# detail=generate_error_message_for_api_keys(ve),
|
||||
# status_code=400,
|
||||
# )
|
||||
# except Exception as e:
|
||||
# raise HTTPException(detail=f"Error occurred: {e}", status_code=400)
|
||||
|
||||
|
||||
@app.post(
|
||||
"/{app_id}/deploy",
|
||||
tags=["Apps"],
|
||||
response_model=DefaultResponse,
|
||||
)
|
||||
async def deploy_app(body: DeployAppRequest, app_id: str, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Query an existing app.\n
|
||||
app_id: The ID of the app. Use "default" for the default app.\n
|
||||
api_key: The API key to use for deployment. If not provided,
|
||||
Embedchain will use the API key previously used (if any).\n
|
||||
"""
|
||||
try:
|
||||
if app_id is None:
|
||||
raise HTTPException(
|
||||
detail="App ID not provided. If you want to use the default app, use 'default' as the app_id.",
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
db_app = get_app(db, app_id)
|
||||
|
||||
if db_app is None:
|
||||
raise HTTPException(detail=f"App with id {app_id} does not exist, please create it first.", status_code=400)
|
||||
|
||||
app = App.from_config(yaml_path=db_app.config)
|
||||
|
||||
api_key = body.api_key
|
||||
# this will save the api key in the embedchain.db
|
||||
Client(api_key=api_key)
|
||||
|
||||
app.deploy()
|
||||
return DefaultResponse(response="App deployed successfully.")
|
||||
except ValueError as ve:
|
||||
if "OPENAI_API_KEY" in str(ve) or "OPENAI_ORGANIZATION" in str(ve):
|
||||
raise HTTPException(
|
||||
detail=generate_error_message_for_api_keys(ve),
|
||||
status_code=400,
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(detail=f"Error occurred: {e}", status_code=400)
|
||||
|
||||
|
||||
@app.delete(
|
||||
"/{app_id}/delete",
|
||||
tags=["Apps"],
|
||||
response_model=DefaultResponse,
|
||||
)
|
||||
async def delete_app(app_id: str, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Delete an existing app.\n
|
||||
app_id: The ID of the app to be deleted.
|
||||
"""
|
||||
try:
|
||||
if app_id is None:
|
||||
raise HTTPException(
|
||||
detail="App ID not provided. If you want to use the default app, use 'default' as the app_id.",
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
db_app = get_app(db, app_id)
|
||||
|
||||
if db_app is None:
|
||||
raise HTTPException(detail=f"App with id {app_id} does not exist, please create it first.", status_code=400)
|
||||
|
||||
app = App.from_config(yaml_path=db_app.config)
|
||||
|
||||
# reset app.db
|
||||
app.db.reset()
|
||||
|
||||
remove_app(db, app_id)
|
||||
return DefaultResponse(response=f"App with id {app_id} deleted successfully.")
|
||||
except Exception as e:
|
||||
raise HTTPException(detail=f"Error occurred: {e}", status_code=400)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
is_dev = os.getenv("DEVELOPMENT", "False")
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=bool(is_dev))
|
||||
45
examples/rest-api/models.py
Normal file
45
examples/rest-api/models.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy import Column, String, Integer
|
||||
from database import Base
|
||||
|
||||
|
||||
class QueryApp(BaseModel):
|
||||
query: str = Field("", description="The query that you want to ask the App.")
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"example": {
|
||||
"query": "Who is Elon Musk?",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SourceApp(BaseModel):
|
||||
source: str = Field("", description="The source that you want to add to the App.")
|
||||
data_type: Optional[str] = Field("", description="The type of data to add, remove it for autosense.")
|
||||
|
||||
model_config = {"json_schema_extra": {"example": {"source": "https://en.wikipedia.org/wiki/Elon_Musk"}}}
|
||||
|
||||
|
||||
class DeployAppRequest(BaseModel):
|
||||
api_key: str = Field("", description="The Embedchain API key for App deployments.")
|
||||
|
||||
model_config = {"json_schema_extra": {"example": {"api_key": "ec-xxx"}}}
|
||||
|
||||
|
||||
class MessageApp(BaseModel):
|
||||
message: str = Field("", description="The message that you want to send to the App.")
|
||||
|
||||
|
||||
class DefaultResponse(BaseModel):
|
||||
response: str
|
||||
|
||||
|
||||
class AppModel(Base):
|
||||
__tablename__ = "apps"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
app_id = Column(String, unique=True, index=True)
|
||||
config = Column(String, unique=True, index=True)
|
||||
5
examples/rest-api/requirements.txt
Normal file
5
examples/rest-api/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
fastapi==0.104.0
|
||||
uvicorn==0.23.2
|
||||
embedchain==0.0.86
|
||||
embedchain[dataloaders]==0.0.86
|
||||
sqlalchemy==2.0.22
|
||||
33
examples/rest-api/sample-config.yaml
Normal file
33
examples/rest-api/sample-config.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
app:
|
||||
config:
|
||||
id: 'default-app'
|
||||
|
||||
llm:
|
||||
provider: openai
|
||||
config:
|
||||
model: 'gpt-3.5-turbo'
|
||||
temperature: 0.5
|
||||
max_tokens: 1000
|
||||
top_p: 1
|
||||
stream: false
|
||||
template: |
|
||||
Use the following pieces of context to answer the query at the end.
|
||||
If you don't know the answer, just say that you don't know, don't try to make up an answer.
|
||||
|
||||
$context
|
||||
|
||||
Query: $query
|
||||
|
||||
Helpful Answer:
|
||||
|
||||
vectordb:
|
||||
provider: chroma
|
||||
config:
|
||||
collection_name: 'rest-api-app'
|
||||
dir: db
|
||||
allow_reset: true
|
||||
|
||||
embedder:
|
||||
provider: openai
|
||||
config:
|
||||
model: 'text-embedding-ada-002'
|
||||
26
examples/rest-api/services.py
Normal file
26
examples/rest-api/services.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from models import AppModel
|
||||
|
||||
|
||||
def get_app(db: Session, app_id: str):
|
||||
return db.query(AppModel).filter(AppModel.app_id == app_id).first()
|
||||
|
||||
|
||||
def get_apps(db: Session, skip: int = 0, limit: int = 100):
|
||||
return db.query(AppModel).offset(skip).limit(limit).all()
|
||||
|
||||
|
||||
def save_app(db: Session, app_id: str, config: str):
|
||||
db_app = AppModel(app_id=app_id, config=config)
|
||||
db.add(db_app)
|
||||
db.commit()
|
||||
db.refresh(db_app)
|
||||
return db_app
|
||||
|
||||
|
||||
def remove_app(db: Session, app_id: str):
|
||||
db_app = db.query(AppModel).filter(AppModel.app_id == app_id).first()
|
||||
db.delete(db_app)
|
||||
db.commit()
|
||||
return db_app
|
||||
21
examples/rest-api/utils.py
Normal file
21
examples/rest-api/utils.py
Normal file
@@ -0,0 +1,21 @@
|
||||
def generate_error_message_for_api_keys(error: ValueError) -> str:
|
||||
env_mapping = {
|
||||
"OPENAI_API_KEY": "OPENAI_API_KEY",
|
||||
"OPENAI_API_TYPE": "OPENAI_API_TYPE",
|
||||
"OPENAI_API_BASE": "OPENAI_API_BASE",
|
||||
"OPENAI_API_VERSION": "OPENAI_API_VERSION",
|
||||
"COHERE_API_KEY": "COHERE_API_KEY",
|
||||
"ANTHROPIC_API_KEY": "ANTHROPIC_API_KEY",
|
||||
"JINACHAT_API_KEY": "JINACHAT_API_KEY",
|
||||
"HUGGINGFACE_ACCESS_TOKEN": "HUGGINGFACE_ACCESS_TOKEN",
|
||||
"REPLICATE_API_TOKEN": "REPLICATE_API_TOKEN",
|
||||
}
|
||||
|
||||
missing_keys = [env_mapping[key] for key in env_mapping if key in str(error)]
|
||||
if missing_keys:
|
||||
missing_keys_str = ", ".join(missing_keys)
|
||||
return f"""Please set the {missing_keys_str} environment variable(s) when running the Docker container.
|
||||
Example: `docker run -e {missing_keys[0]}=xxx embedchain/app:rest-api-latest`
|
||||
"""
|
||||
else:
|
||||
return "Unknown error occurred."
|
||||
Reference in New Issue
Block a user