From 8dd5cb9602e9982d4d74a7811ea29078a287d878 Mon Sep 17 00:00:00 2001 From: Sidharth Mohanty Date: Fri, 3 Nov 2023 13:02:51 +0530 Subject: [PATCH] Add `rest-api` example (#889) --- .gitignore | 1 - README.md | 8 + configs/anthropic.yaml | 2 +- configs/azure_openai.yaml | 2 +- configs/chroma.yaml | 2 +- configs/cohere.yaml | 2 +- configs/full-stack.yaml | 2 +- configs/huggingface.yaml | 2 +- configs/llama2.yaml | 2 +- configs/opensearch.yaml | 2 +- configs/vertexai.yaml | 2 +- .../add-datasource-to-an-app.mdx | 3 + docs/api-reference/chat-with-an-app.mdx | 3 + docs/api-reference/check-status.mdx | 3 + docs/api-reference/create-app.mdx | 3 + docs/api-reference/delete-app.mdx | 3 + docs/api-reference/deploy-app.mdx | 3 + docs/api-reference/get-all-apps.mdx | 3 + ...get-datasources-associated-with-app-id.mdx | 3 + docs/api-reference/getting-started.mdx | 102 +++++ docs/api-reference/query-an-app.mdx | 3 + docs/examples/api_server.mdx | 93 ---- docs/mint.json | 53 ++- docs/rest-api.json | 428 ++++++++++++++++++ embedchain/pipeline.py | 11 +- examples/rest-api/.dockerignore | 4 + examples/rest-api/.gitignore | 4 + examples/rest-api/Dockerfile | 15 + examples/rest-api/README.md | 21 + examples/rest-api/__init__.py | 0 .../rest-api/bruno/ec-rest-api/bruno.json | 5 + .../bruno/ec-rest-api/default_add.bru | 18 + .../bruno/ec-rest-api/default_chat.bru | 17 + .../bruno/ec-rest-api/default_query.bru | 17 + examples/rest-api/bruno/ec-rest-api/ping.bru | 11 + examples/rest-api/configs/README.md | 3 + examples/rest-api/database.py | 11 + examples/rest-api/default.yaml | 17 + examples/rest-api/main.py | 321 +++++++++++++ examples/rest-api/models.py | 45 ++ examples/rest-api/requirements.txt | 5 + examples/rest-api/sample-config.yaml | 33 ++ examples/rest-api/services.py | 26 ++ examples/rest-api/utils.py | 21 + 44 files changed, 1217 insertions(+), 118 deletions(-) create mode 100644 docs/api-reference/add-datasource-to-an-app.mdx create mode 100644 docs/api-reference/chat-with-an-app.mdx create mode 100644 docs/api-reference/check-status.mdx create mode 100644 docs/api-reference/create-app.mdx create mode 100644 docs/api-reference/delete-app.mdx create mode 100644 docs/api-reference/deploy-app.mdx create mode 100644 docs/api-reference/get-all-apps.mdx create mode 100644 docs/api-reference/get-datasources-associated-with-app-id.mdx create mode 100644 docs/api-reference/getting-started.mdx create mode 100644 docs/api-reference/query-an-app.mdx delete mode 100644 docs/examples/api_server.mdx create mode 100644 docs/rest-api.json create mode 100644 examples/rest-api/.dockerignore create mode 100644 examples/rest-api/.gitignore create mode 100644 examples/rest-api/Dockerfile create mode 100644 examples/rest-api/README.md create mode 100644 examples/rest-api/__init__.py create mode 100644 examples/rest-api/bruno/ec-rest-api/bruno.json create mode 100644 examples/rest-api/bruno/ec-rest-api/default_add.bru create mode 100644 examples/rest-api/bruno/ec-rest-api/default_chat.bru create mode 100644 examples/rest-api/bruno/ec-rest-api/default_query.bru create mode 100644 examples/rest-api/bruno/ec-rest-api/ping.bru create mode 100644 examples/rest-api/configs/README.md create mode 100644 examples/rest-api/database.py create mode 100644 examples/rest-api/default.yaml create mode 100644 examples/rest-api/main.py create mode 100644 examples/rest-api/models.py create mode 100644 examples/rest-api/requirements.txt create mode 100644 examples/rest-api/sample-config.yaml create mode 100644 examples/rest-api/services.py create mode 100644 examples/rest-api/utils.py diff --git a/.gitignore b/.gitignore index b6df6ac0..3165bee0 100644 --- a/.gitignore +++ b/.gitignore @@ -76,7 +76,6 @@ docs/_build/ target/ # Jupyter Notebook -*.yaml # IPython profile_default/ diff --git a/README.md b/README.md index 958ae0af..d40b378a 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,14 @@ Book a [1-on-1 Session](https://cal.com/taranjeetio/ec) with Taranjeet, the foun pip install --upgrade embedchain ``` +To run Embedchain as a REST API server use, + +```bash +docker run -d --name embedchain -p 8000:8000 embedchain/app:rest-api-latest +``` + +Navigate to http://0.0.0.0:8000/docs to interact with the API. + ## 🔍 Demo Try out embedchain in your browser: diff --git a/configs/anthropic.yaml b/configs/anthropic.yaml index e3b50ecf..395125f9 100644 --- a/configs/anthropic.yaml +++ b/configs/anthropic.yaml @@ -1,7 +1,7 @@ llm: provider: anthropic - model: 'claude-instant-1' config: + model: 'claude-instant-1' temperature: 0.5 max_tokens: 1000 top_p: 1 diff --git a/configs/azure_openai.yaml b/configs/azure_openai.yaml index ef77925e..50eaff0c 100644 --- a/configs/azure_openai.yaml +++ b/configs/azure_openai.yaml @@ -4,8 +4,8 @@ app: llm: provider: azure_openai - model: gpt-35-turbo config: + model: gpt-35-turbo deployment_name: your_llm_deployment_name temperature: 0.5 max_tokens: 1000 diff --git a/configs/chroma.yaml b/configs/chroma.yaml index 7b340b9e..5b9de0b3 100644 --- a/configs/chroma.yaml +++ b/configs/chroma.yaml @@ -5,8 +5,8 @@ app: llm: provider: openai - model: 'gpt-3.5-turbo' config: + model: 'gpt-3.5-turbo' temperature: 0.5 max_tokens: 1000 top_p: 1 diff --git a/configs/cohere.yaml b/configs/cohere.yaml index 8a799506..0edd4e8f 100644 --- a/configs/cohere.yaml +++ b/configs/cohere.yaml @@ -1,7 +1,7 @@ llm: provider: cohere - model: large config: + model: large temperature: 0.5 max_tokens: 1000 top_p: 1 diff --git a/configs/full-stack.yaml b/configs/full-stack.yaml index 4c9d4bba..ec3c7a2e 100644 --- a/configs/full-stack.yaml +++ b/configs/full-stack.yaml @@ -4,8 +4,8 @@ app: llm: provider: openai - model: 'gpt-3.5-turbo' config: + model: 'gpt-3.5-turbo' temperature: 0.5 max_tokens: 1000 top_p: 1 diff --git a/configs/huggingface.yaml b/configs/huggingface.yaml index f9347f68..508c9d77 100644 --- a/configs/huggingface.yaml +++ b/configs/huggingface.yaml @@ -1,7 +1,7 @@ llm: provider: huggingface - model: 'google/flan-t5-xxl' config: + model: 'google/flan-t5-xxl' temperature: 0.5 max_tokens: 1000 top_p: 0.5 diff --git a/configs/llama2.yaml b/configs/llama2.yaml index fb9f5002..61b3b925 100644 --- a/configs/llama2.yaml +++ b/configs/llama2.yaml @@ -1,7 +1,7 @@ llm: provider: llama2 - model: 'a16z-infra/llama13b-v2-chat:df7690f1994d94e96ad9d568eac121aecf50684a0b0963b25a41cc40061269e5' config: + model: 'a16z-infra/llama13b-v2-chat:df7690f1994d94e96ad9d568eac121aecf50684a0b0963b25a41cc40061269e5' temperature: 0.5 max_tokens: 1000 top_p: 0.5 diff --git a/configs/opensearch.yaml b/configs/opensearch.yaml index 07cf1c2d..23c3e76e 100644 --- a/configs/opensearch.yaml +++ b/configs/opensearch.yaml @@ -7,8 +7,8 @@ app: llm: provider: openai - model: 'gpt-3.5-turbo' config: + model: 'gpt-3.5-turbo' temperature: 0.5 max_tokens: 1000 top_p: 1 diff --git a/configs/vertexai.yaml b/configs/vertexai.yaml index 83e7a683..f303654c 100644 --- a/configs/vertexai.yaml +++ b/configs/vertexai.yaml @@ -1,6 +1,6 @@ llm: provider: vertexai - model: 'chat-bison' config: + model: 'chat-bison' temperature: 0.5 top_p: 0.5 diff --git a/docs/api-reference/add-datasource-to-an-app.mdx b/docs/api-reference/add-datasource-to-an-app.mdx new file mode 100644 index 00000000..28f845ea --- /dev/null +++ b/docs/api-reference/add-datasource-to-an-app.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /{app_id}/add +--- \ No newline at end of file diff --git a/docs/api-reference/chat-with-an-app.mdx b/docs/api-reference/chat-with-an-app.mdx new file mode 100644 index 00000000..2571bf71 --- /dev/null +++ b/docs/api-reference/chat-with-an-app.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /{app_id}/chat +--- \ No newline at end of file diff --git a/docs/api-reference/check-status.mdx b/docs/api-reference/check-status.mdx new file mode 100644 index 00000000..bbad0317 --- /dev/null +++ b/docs/api-reference/check-status.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /ping +--- \ No newline at end of file diff --git a/docs/api-reference/create-app.mdx b/docs/api-reference/create-app.mdx new file mode 100644 index 00000000..e18320a6 --- /dev/null +++ b/docs/api-reference/create-app.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /create +--- \ No newline at end of file diff --git a/docs/api-reference/delete-app.mdx b/docs/api-reference/delete-app.mdx new file mode 100644 index 00000000..c81d2555 --- /dev/null +++ b/docs/api-reference/delete-app.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /{app_id}/delete +--- \ No newline at end of file diff --git a/docs/api-reference/deploy-app.mdx b/docs/api-reference/deploy-app.mdx new file mode 100644 index 00000000..830b804e --- /dev/null +++ b/docs/api-reference/deploy-app.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /{app_id}/deploy +--- \ No newline at end of file diff --git a/docs/api-reference/get-all-apps.mdx b/docs/api-reference/get-all-apps.mdx new file mode 100644 index 00000000..19881d05 --- /dev/null +++ b/docs/api-reference/get-all-apps.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /apps +--- \ No newline at end of file diff --git a/docs/api-reference/get-datasources-associated-with-app-id.mdx b/docs/api-reference/get-datasources-associated-with-app-id.mdx new file mode 100644 index 00000000..c5eb6789 --- /dev/null +++ b/docs/api-reference/get-datasources-associated-with-app-id.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /{app_id}/data +--- \ No newline at end of file diff --git a/docs/api-reference/getting-started.mdx b/docs/api-reference/getting-started.mdx new file mode 100644 index 00000000..88917a37 --- /dev/null +++ b/docs/api-reference/getting-started.mdx @@ -0,0 +1,102 @@ +--- +title: "🌍 Getting Started" +--- + +## Quick Start + +To run Embedchain as a REST API server use, + +```bash +docker run -d --name embedchain -p 8000:8000 embedchain/app:rest-api-latest +``` + +Open up your browser and navigate to http://0.0.0.0:8000/docs to interact with the API. There is a full-fledged Swagger docs playground with all the information +about the API endpoints. + +![Swagger Docs Screenshot](https://github.com/embedchain/embedchain/assets/73601258/299d81e5-a0df-407c-afc2-6fa2c4286844) + +## Creating your first App + +App requires an `app_id` to be created. The `app_id` is a unique identifier for your app. + +By default we will use the opensource **gpt4all** model to perform operations. You can also specify your own config by uploading a config YAML file. + +For example, create a `config.yaml` file (adjust according to your requirements): + +```yaml +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" +``` + +To learn more about custom configurations, check out the [Custom configurations](https://docs.embedchain.ai/advanced/configuration). +To explore more examples of config YAMLs for Embedchain, visit [embedchain/configs](https://github.com/embedchain/embedchain/tree/main/configs). + +Now, you can upload this config file in the request body. + +**Note:** To use custom models, an **API key** might be required. Refer to the table below to determine the necessary API key for your provider. + +| Keys | Providers | +| -------------------------- | ------------------------------ | +| `OPENAI_API_KEY ` | OpenAI, Azure OpenAI, Jina etc | +| `OPENAI_API_TYPE` | Azure OpenAI | +| `OPENAI_API_BASE` | Azure OpenAI | +| `OPENAI_API_VERSION` | Azure OpenAI | +| `COHERE_API_KEY` | Cohere | +| `ANTHROPIC_API_KEY` | Anthropic | +| `JINACHAT_API_KEY` | Jina | +| `HUGGINGFACE_ACCESS_TOKEN` | Huggingface | +| `REPLICATE_API_TOKEN` | LLAMA2 | + +To provide them, you can simply run the docker command with the `-e` flag. + +For example, + +```bash +docker run -d --name embedchain -p 8000:8000 -e OPENAI_API_KEY=YOUR_API_KEY embedchain/app:rest-api-latest +``` + +Cool! This will create a new Embedchain App with the given `app_id`. + +## Deploying your App to Embedchain Platform + +This feature is very powerful as it allows the creation of a public API endpoint for your app, enabling queries from anywhere. This creates a _pipeline_ +for your app that can sync the data time to time and provide you with the best results. + +![My first Pipeline](https://github.com/embedchain/embedchain/assets/73601258/266a66e0-330e-4bb9-aa97-687d826cd3fa) + +To utilize this functionality, visit app.embedchain.ai and create an account. Subsequently, generate a new [API KEY](https://app.embedchain.ai/settings/keys/). + +![Create Embedchain API Key](https://github.com/embedchain/embedchain/assets/73601258/791e92cc-4a6f-4740-94c2-f11cce13e93b) + +Using this API key, you can deploy your app to the platform. diff --git a/docs/api-reference/query-an-app.mdx b/docs/api-reference/query-an-app.mdx new file mode 100644 index 00000000..c72e3851 --- /dev/null +++ b/docs/api-reference/query-an-app.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /{app_id}/query +--- \ No newline at end of file diff --git a/docs/examples/api_server.mdx b/docs/examples/api_server.mdx deleted file mode 100644 index 85dfc358..00000000 --- a/docs/examples/api_server.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: '🌍 API Server' ---- - -The API server example can be found [here](https://github.com/embedchain/embedchain/tree/main/examples/api_server). - -It is a Flask based server that integrates the `embedchain` package, offering endpoints to add, query, and chat to engage in conversations with a chatbot using JSON requests. - -### 🐳 Docker Setup - -- Open variables.env, and edit it to add your 🔑 `OPENAI_API_KEY`. -- To setup your api server using docker, run the following command inside this folder using your terminal. - -```bash -docker-compose up --build -``` - -📝 Note: The build command might take a while to install all the packages depending on your system resources. - -### 🚀 Usage Instructions - -- Your api server is running on [http://localhost:5000/](http://localhost:5000/) -- To use the api server, make an api call to the endpoints `/add`, `/query` and `/chat` using the json formats discussed below. -- To add data sources to the bot (/add): -```json -// Request -{ - "data_type": "your_data_type_here", - "url_or_text": "your_url_or_text_here" -} - -// Response -{ - "data": "Added data_type: url_or_text" -} -``` -- To ask queries from the bot (/query): -```json -// Request -{ - "question": "your_question_here" -} - -// Response -{ - "data": "your_answer_here" -} -``` -- To chat with the bot (/chat): -```json -// Request -{ - "question": "your_question_here" -} - -// Response -{ - "data": "your_answer_here" -} -``` - -### 📡 Curl Call Formats - -- To add data sources to the bot (/add): -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -d '{ - "data_type": "your_data_type_here", - "url_or_text": "your_url_or_text_here" - }' \ - http://localhost:5000/add -``` -- To ask queries from the bot (/query): -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -d '{ - "question": "your_question_here" - }' \ - http://localhost:5000/query -``` -- To chat with the bot (/chat): -```bash -curl -X POST \ - -H "Content-Type: application/json" \ - -d '{ - "question": "your_question_here" - }' \ - http://localhost:5000/chat -``` - -🎉 Happy Chatting! 🎉 diff --git a/docs/mint.json b/docs/mint.json index ab31ded3..00cd777e 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -14,6 +14,7 @@ "dark": "#020415" } }, + "openapi": ["/rest-api.json"], "metadata": { "og:image": "/images/og.png", "twitter:site": "@embedchain" @@ -28,8 +29,8 @@ "url": "https://twitter.com/embedchain" }, { - "name":"Slack", - "url":"https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw" + "name": "Slack", + "url": "https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw" }, { "name": "Discord", @@ -46,11 +47,20 @@ "navigation": [ { "group": "Get started", - "pages": ["get-started/quickstart", "get-started/introduction", "get-started/faq", "get-started/examples"] + "pages": [ + "get-started/quickstart", + "get-started/introduction", + "get-started/faq", + "get-started/examples" + ] }, { "group": "Components", - "pages": ["components/llms", "components/embedding-models", "components/vector-databases"] + "pages": [ + "components/llms", + "components/embedding-models", + "components/vector-databases" + ] }, { "group": "Data sources", @@ -81,16 +91,34 @@ "group": "Advanced", "pages": ["advanced/configuration"] }, + { + "group": "Rest API", + "pages": [ + "api-reference/getting-started", + "api-reference/check-status", + "api-reference/get-all-apps", + "api-reference/create-app", + "api-reference/query-an-app", + "api-reference/add-datasource-to-an-app", + "api-reference/get-datasources-associated-with-app-id", + "api-reference/deploy-app", + "api-reference/delete-app" + ] + }, { "group": "Examples", - "pages": ["examples/full_stack", "examples/api_server", "examples/discord_bot", "examples/slack_bot", "examples/telegram_bot", "examples/whatsapp_bot", "examples/poe_bot"] + "pages": [ + "examples/full_stack", + "examples/discord_bot", + "examples/slack_bot", + "examples/telegram_bot", + "examples/whatsapp_bot", + "examples/poe_bot" + ] }, { "group": "Community", - "pages": [ - "community/connect-with-us", - "community/showcase" - ] + "pages": ["community/connect-with-us", "community/showcase"] }, { "group": "Integrations", @@ -108,16 +136,13 @@ }, { "group": "Product", - "pages": [ - "product/release-notes" - ] + "pages": ["product/release-notes"] } - ], "footerSocials": { "website": "https://embedchain.ai", "github": "https://github.com/embedchain/embedchain", - "slack":"https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw", + "slack": "https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw", "discord": "https://discord.gg/6PzXDgEjG5", "twitter": "https://twitter.com/embedchain", "linkedin": "https://www.linkedin.com/company/embedchain" diff --git a/docs/rest-api.json b/docs/rest-api.json new file mode 100644 index 00000000..e35529fb --- /dev/null +++ b/docs/rest-api.json @@ -0,0 +1,428 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Embedchain REST API", + "description": "This is the REST API for Embedchain.", + "license": { + "name": "Apache 2.0", + "url": "https://github.com/embedchain/embedchain/blob/main/LICENSE" + }, + "version": "0.0.1" + }, + "paths": { + "/ping": { + "get": { + "tags": ["Utility"], + "summary": "Check Status", + "description": "Endpoint to check the status of the API.", + "operationId": "check_status_ping_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { "application/json": { "schema": {} } } + } + } + } + }, + "/apps": { + "get": { + "tags": ["Apps"], + "summary": "Get All Apps", + "description": "Get all apps.", + "operationId": "get_all_apps_apps_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { "application/json": { "schema": {} } } + } + } + } + }, + "/create": { + "post": { + "tags": ["Apps"], + "summary": "Create App", + "description": "Create a new app using App ID.", + "operationId": "create_app_using_default_config_create_post", + "parameters": [ + { + "name": "app_id", + "in": "query", + "required": true, + "schema": { "type": "string", "title": "App Id" } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Body_create_app_using_default_config_create_post" + } + ], + "title": "Body" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DefaultResponse" } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } + } + }, + "/{app_id}/data": { + "get": { + "tags": ["Apps"], + "summary": "Get Datasources Associated With App Id", + "description": "Get all datasources for an app.", + "operationId": "get_datasources_associated_with_app_id__app_id__data_get", + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { "type": "string", "title": "App Id" } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { "application/json": { "schema": {} } } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } + } + }, + "/{app_id}/add": { + "post": { + "tags": ["Apps"], + "summary": "Add Datasource To An App", + "description": "Add a source to an existing app.", + "operationId": "add_datasource_to_an_app__app_id__add_post", + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { "type": "string", "title": "App Id" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/SourceApp" } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DefaultResponse" } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } + } + }, + "/{app_id}/query": { + "post": { + "tags": ["Apps"], + "summary": "Query An App", + "description": "Query an existing app.", + "operationId": "query_an_app__app_id__query_post", + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { "type": "string", "title": "App Id" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/QueryApp" } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DefaultResponse" } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } + } + }, + "/{app_id}/chat": { + "post": { + "tags": ["Apps"], + "summary": "Chat With An App", + "description": "Query an existing app.\n\napp_id: The ID of the app. Use \"default\" for the default app.\n\nmessage: The message that you want to send to the App.", + "operationId": "chat_with_an_app__app_id__chat_post", + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { "type": "string", "title": "App Id" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/MessageApp" } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DefaultResponse" } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } + } + }, + "/{app_id}/deploy": { + "post": { + "tags": ["Apps"], + "summary": "Deploy App", + "description": "Deploy an existing app.", + "operationId": "deploy_app__app_id__deploy_post", + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { "type": "string", "title": "App Id" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DeployAppRequest" } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DefaultResponse" } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } + } + }, + "/{app_id}/delete": { + "delete": { + "tags": ["Apps"], + "summary": "Delete App", + "description": "Delete an existing app.", + "operationId": "delete_app__app_id__delete_delete", + "parameters": [ + { + "name": "app_id", + "in": "path", + "required": true, + "schema": { "type": "string", "title": "App Id" } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/DefaultResponse" } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Body_create_app_using_default_config_create_post": { + "properties": { + "config": { "type": "string", "format": "binary", "title": "Config" } + }, + "type": "object", + "title": "Body_create_app_using_default_config_create_post" + }, + "DefaultResponse": { + "properties": { "response": { "type": "string", "title": "Response" } }, + "type": "object", + "required": ["response"], + "title": "DefaultResponse" + }, + "DeployAppRequest": { + "properties": { + "api_key": { + "type": "string", + "title": "Api Key", + "description": "The Embedchain API key for App deployments.", + "default": "" + } + }, + "type": "object", + "title": "DeployAppRequest", + "example":{ + "api_key":"ec-xxx" + } + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { "$ref": "#/components/schemas/ValidationError" }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "MessageApp": { + "properties": { + "message": { + "type": "string", + "title": "Message", + "description": "The message that you want to send to the App.", + "default": "" + } + }, + "type": "object", + "title": "MessageApp" + }, + "QueryApp": { + "properties": { + "query": { + "type": "string", + "title": "Query", + "description": "The query that you want to ask the App.", + "default": "" + } + }, + "type": "object", + "title": "QueryApp", + "example":{ + "query":"Who is Elon Musk?" + } + }, + "SourceApp": { + "properties": { + "source": { + "type": "string", + "title": "Source", + "description": "The source that you want to add to the App.", + "default": "" + }, + "data_type": { + "anyOf": [{ "type": "string" }, { "type": "null" }], + "title": "Data Type", + "description": "The type of data to add, remove it if you want Embedchain to detect it automatically.", + "default": "" + } + }, + "type": "object", + "title": "SourceApp", + "example":{ + "source":"https://en.wikipedia.org/wiki/Elon_Musk" + } + }, + "ValidationError": { + "properties": { + "loc": { + "items": { "anyOf": [{ "type": "string" }, { "type": "integer" }] }, + "type": "array", + "title": "Location" + }, + "msg": { "type": "string", "title": "Message" }, + "type": { "type": "string", "title": "Error Type" } + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError" + } + } + } + } + \ No newline at end of file diff --git a/embedchain/pipeline.py b/embedchain/pipeline.py index c0a35c3a..232496b7 100644 --- a/embedchain/pipeline.py +++ b/embedchain/pipeline.py @@ -313,6 +313,15 @@ class Pipeline(EmbedChain): ) self.connection.commit() + def get_data_sources(self): + db_data = self.cursor.execute("SELECT * FROM data_sources WHERE pipeline_id = ?", (self.local_id,)).fetchall() + + data_sources = [] + for data in db_data: + data_sources.append({"data_type": data[2], "data_value": data[3], "metadata": data[4]}) + + return data_sources + def deploy(self): if self.client is None: self._init_client() @@ -348,7 +357,7 @@ class Pipeline(EmbedChain): with open(yaml_path, "r") as file: config_data = yaml.safe_load(file) - pipeline_config_data = config_data.get("pipeline", {}).get("config", {}) + pipeline_config_data = config_data.get("app", {}).get("config", {}) db_config_data = config_data.get("vectordb", {}) embedding_model_config_data = config_data.get("embedding_model", {}) llm_config_data = config_data.get("llm", {}) diff --git a/examples/rest-api/.dockerignore b/examples/rest-api/.dockerignore new file mode 100644 index 00000000..9d8eb1ab --- /dev/null +++ b/examples/rest-api/.dockerignore @@ -0,0 +1,4 @@ +.env +app.db +configs/**.yaml +db \ No newline at end of file diff --git a/examples/rest-api/.gitignore b/examples/rest-api/.gitignore new file mode 100644 index 00000000..60d5cd37 --- /dev/null +++ b/examples/rest-api/.gitignore @@ -0,0 +1,4 @@ +.env +app.db +configs/**.yaml +db diff --git a/examples/rest-api/Dockerfile b/examples/rest-api/Dockerfile new file mode 100644 index 00000000..9d9c58c6 --- /dev/null +++ b/examples/rest-api/Dockerfile @@ -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"] diff --git a/examples/rest-api/README.md b/examples/rest-api/README.md new file mode 100644 index 00000000..1e5b9571 --- /dev/null +++ b/examples/rest-api/README.md @@ -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 +``` + diff --git a/examples/rest-api/__init__.py b/examples/rest-api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/rest-api/bruno/ec-rest-api/bruno.json b/examples/rest-api/bruno/ec-rest-api/bruno.json new file mode 100644 index 00000000..ed86683c --- /dev/null +++ b/examples/rest-api/bruno/ec-rest-api/bruno.json @@ -0,0 +1,5 @@ +{ + "version": "1", + "name": "ec-rest-api", + "type": "collection" +} \ No newline at end of file diff --git a/examples/rest-api/bruno/ec-rest-api/default_add.bru b/examples/rest-api/bruno/ec-rest-api/default_add.bru new file mode 100644 index 00000000..06d2e8ef --- /dev/null +++ b/examples/rest-api/bruno/ec-rest-api/default_add.bru @@ -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" + } +} diff --git a/examples/rest-api/bruno/ec-rest-api/default_chat.bru b/examples/rest-api/bruno/ec-rest-api/default_chat.bru new file mode 100644 index 00000000..d0fde8d5 --- /dev/null +++ b/examples/rest-api/bruno/ec-rest-api/default_chat.bru @@ -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" + } +} diff --git a/examples/rest-api/bruno/ec-rest-api/default_query.bru b/examples/rest-api/bruno/ec-rest-api/default_query.bru new file mode 100644 index 00000000..95947583 --- /dev/null +++ b/examples/rest-api/bruno/ec-rest-api/default_query.bru @@ -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?" + } +} diff --git a/examples/rest-api/bruno/ec-rest-api/ping.bru b/examples/rest-api/bruno/ec-rest-api/ping.bru new file mode 100644 index 00000000..5deddbfe --- /dev/null +++ b/examples/rest-api/bruno/ec-rest-api/ping.bru @@ -0,0 +1,11 @@ +meta { + name: ping + type: http + seq: 1 +} + +get { + url: http://localhost:8000/ping + body: json + auth: none +} diff --git a/examples/rest-api/configs/README.md b/examples/rest-api/configs/README.md new file mode 100644 index 00000000..bf4bbc9e --- /dev/null +++ b/examples/rest-api/configs/README.md @@ -0,0 +1,3 @@ +### Config directory + +Here, all the YAML files will get stored. diff --git a/examples/rest-api/database.py b/examples/rest-api/database.py new file mode 100644 index 00000000..3eaafc95 --- /dev/null +++ b/examples/rest-api/database.py @@ -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() diff --git a/examples/rest-api/default.yaml b/examples/rest-api/default.yaml new file mode 100644 index 00000000..6ed62d1e --- /dev/null +++ b/examples/rest-api/default.yaml @@ -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' diff --git a/examples/rest-api/main.py b/examples/rest-api/main.py new file mode 100644 index 00000000..c871f707 --- /dev/null +++ b/examples/rest-api/main.py @@ -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)) diff --git a/examples/rest-api/models.py b/examples/rest-api/models.py new file mode 100644 index 00000000..7a359852 --- /dev/null +++ b/examples/rest-api/models.py @@ -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) diff --git a/examples/rest-api/requirements.txt b/examples/rest-api/requirements.txt new file mode 100644 index 00000000..9d2aa3fd --- /dev/null +++ b/examples/rest-api/requirements.txt @@ -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 \ No newline at end of file diff --git a/examples/rest-api/sample-config.yaml b/examples/rest-api/sample-config.yaml new file mode 100644 index 00000000..1e6d60fe --- /dev/null +++ b/examples/rest-api/sample-config.yaml @@ -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' diff --git a/examples/rest-api/services.py b/examples/rest-api/services.py new file mode 100644 index 00000000..af2c984b --- /dev/null +++ b/examples/rest-api/services.py @@ -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 diff --git a/examples/rest-api/utils.py b/examples/rest-api/utils.py new file mode 100644 index 00000000..c6e8f2dd --- /dev/null +++ b/examples/rest-api/utils.py @@ -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."