Skip to content

Commit cc2eb8b

Browse files
Updates to the chatbot rag app (#118)
1 parent 393b790 commit cc2eb8b

File tree

8 files changed

+147
-45
lines changed

8 files changed

+147
-45
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FLASK_APP=api/app.py
2+
FLASK_RUN_PORT=3001

example-apps/chatbot-rag-app/Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ WORKDIR /app
1414
RUN mkdir -p ./frontend/build
1515
COPY --from=build-step ./app/frontend/build ./frontend/build
1616
RUN mkdir ./api
17+
RUN mkdir ./data
1718

1819
RUN apt-get update && apt-get install -y \
1920
build-essential \
@@ -24,10 +25,11 @@ RUN apt-get update && apt-get install -y \
2425

2526

2627
COPY api ./api
28+
COPY data ./data
2729
COPY requirements.txt ./requirements.txt
2830
RUN pip3 install -r ./requirements.txt
2931
ENV FLASK_ENV production
3032

3133
EXPOSE 4000
3234
WORKDIR /app/api
33-
CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0", "--port=4000" ]
35+
CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0", "--port=4000" ]

example-apps/chatbot-rag-app/README.md

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This is a sample app that combines Elasticsearch, Langchain and a number of diff
44

55
![Screenshot of the sample app](./app-demo.gif)
66

7-
## 1. Download the Project
7+
## Download the Project
88

99
Download the project from Github and extract the `chatbot-rag-app` folder.
1010

@@ -13,7 +13,7 @@ curl https://codeload.github.com/elastic/elasticsearch-labs/tar.gz/main | \
1313
tar -xz --strip=2 elasticsearch-labs-main/example-apps/chatbot-rag-app
1414
```
1515

16-
## 2. Installing and connecting to Elasticsearch
16+
## Installing and connecting to Elasticsearch
1717

1818
### Install Elasticsearch
1919

@@ -29,6 +29,8 @@ export ELASTIC_USERNAME=...
2929
export ELASTIC_PASSWORD=...
3030
```
3131

32+
You can add these to a *.env* file for convenience. See the *env.example* file for a .env file template.
33+
3234
### Change the Elasticsearch index and chat_history index
3335

3436
By default, the app will use the `workplace-app-docs` index and the chat history index will be `workplace-app-docs-chat-history`. If you want to change these, you can set the following environment variables:
@@ -38,7 +40,7 @@ ES_INDEX=workplace-app-docs
3840
ES_INDEX_CHAT_HISTORY=workplace-app-docs-chat-history
3941
```
4042

41-
## 3. Connecting to LLM
43+
## Connecting to LLM
4244

4345
We support three LLM providers: Azure, OpenAI and Bedrock.
4446

@@ -100,30 +102,12 @@ region=...
100102
To use Vertex AI you need to set the following environment variables. More infos [here](https://python.langchain.com/docs/integrations/llms/google_vertex_ai_palm).
101103

102104
```sh
103-
export LLM_TYPE=vertex
104-
export VERTEX_PROJECT_ID=<gcp-project-id>
105-
export VERTEX_REGION=<gcp-region> # Default is us-central1
106-
export GOOGLE_APPLICATION_CREDENTIALS=<path-json-service-account>
105+
export LLM_TYPE=vertex
106+
export VERTEX_PROJECT_ID=<gcp-project-id>
107+
export VERTEX_REGION=<gcp-region> # Default is us-central1
108+
export GOOGLE_APPLICATION_CREDENTIALS=<path-json-service-account>
107109
```
108110

109-
## 3. Ingest Data
110-
111-
You can index the sample data from the provided .json files in the `data` folder:
112-
113-
```sh
114-
python data/index-data.py
115-
```
116-
117-
by default, this will index the data into the `workplace-app-docs` index. You can change this by setting the `ES_INDEX` environment variable.
118-
119-
### Indexing your own data
120-
121-
`index-data.py` is a simple script that uses Langchain to index data into Elasticsearch, using the `JSONLoader` and `CharacterTextSplitter` to split the large documents into passages. Modify this script to index your own data.
122-
123-
Langchain offers many different ways to index data, if you cant just load it via JSONLoader. See the [Langchain documentation](https://python.langchain.com/docs/modules/data_connection/document_loaders)
124-
125-
Remember to keep the `ES_INDEX` environment variable set to the index you want to index into and to query from.
126-
127111
## Running the App
128112

129113
Once you have indexed data into the Elasticsearch index, there are two ways to run the app: via Docker or locally. Docker is advised for testing & production use. Locally is advised for development.
@@ -136,18 +120,22 @@ Build the Docker image and run it with the following environment variables.
136120
docker build -f Dockerfile -t chatbot-rag-app .
137121
```
138122

139-
Then run it with the following environment variables. In the example below, we are using OpenAI LLM.
123+
#### Ingest data
124+
125+
Make sure you have a *.env* file with all your variables, then run:
126+
127+
```sh
128+
docker run --rm --env-file .env chatbot-rag-app flask create-index
129+
```
130+
131+
See "Ingest data" section under Running Locally for more details about the `flask create-index` command.
132+
133+
#### Run API and frontend
140134

141-
If you're using one of the other LLMs, you will need to set the appropriate environment variables via `-e` flag.
135+
You will need to set the appropriate environment variables in your *.env* file. See the *env.example* file for instructions.
142136

143137
```sh
144-
docker run -p 4000:4000 \
145-
-e "ELASTIC_CLOUD_ID=<cloud_id>" \
146-
-e "ELASTIC_USERNAME=elastic" \
147-
-e "ELASTIC_PASSWORD=<password>" \
148-
-e "LLM_TYPE=openai" \
149-
-e "OPENAI_API_KEY=<openai_key>" \
150-
-d chatbot-rag-app
138+
docker run --rm -p 4000:4000 --env-file .env -d chatbot-rag-app
151139
```
152140

153141
### Locally (for development)
@@ -171,21 +159,37 @@ python -m venv .venv
171159

172160
# Activate the virtual environment
173161
source .venv/bin/activate
174-
```
175162

176-
```sh
177163
# Install Python dependencies
178-
pip install -r requirements.txt
164+
pip install -r api/requirements.txt
179165

180166
# Install Node dependencies
181-
cd frontend && yarn
167+
cd frontend && yarn && cd ..
168+
```
169+
170+
#### Ingest data
171+
172+
You can index the sample data from the provided .json files in the `data` folder:
173+
174+
```sh
175+
flask create-index
182176
```
183177

178+
By default, this will index the data into the `workplace-app-docs` index. You can change this by setting the `ES_INDEX` environment variable.
179+
180+
##### Indexing your own data
181+
182+
The ingesting logic is stored in `data/index-data.py`. This is a simple script that uses Langchain to index data into Elasticsearch, using the `JSONLoader` and `CharacterTextSplitter` to split the large documents into passages. Modify this script to index your own data.
183+
184+
Langchain offers many different ways to index data, if you cant just load it via JSONLoader. See the [Langchain documentation](https://python.langchain.com/docs/modules/data_connection/document_loaders)
185+
186+
Remember to keep the `ES_INDEX` environment variable set to the index you want to index into and to query from.
187+
184188
#### Run API and frontend
185189

186190
```sh
187191
# Launch API app
188-
python api/app.py
192+
flask run
189193

190194
# In a separate terminal launch frontend app
191195
cd frontend && yarn start

example-apps/chatbot-rag-app/api/app.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from uuid import uuid4
55
from chat import chat, ask_question, parse_stream_message
66
import threading
7+
import os
8+
import sys
79

810
app = Flask(__name__, static_folder="../frontend/build", static_url_path="/")
911
CORS(app)
@@ -35,5 +37,15 @@ def api_chat():
3537
)
3638

3739

40+
@app.cli.command()
41+
def create_index():
42+
"""Create or re-create the Elasticsearch index."""
43+
basedir = os.path.abspath(os.path.dirname(__file__))
44+
sys.path.append(f'{basedir}/../')
45+
46+
from data import index_data
47+
index_data.main()
48+
49+
3850
if __name__ == "__main__":
3951
app.run(port=3001, debug=True)

example-apps/chatbot-rag-app/api/chat.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
INDEX_CHAT_HISTORY = os.getenv(
2121
"ES_INDEX_CHAT_HISTORY", "workplace-app-docs-chat-history"
2222
)
23+
ELSER_MODEL = os.getenv("ELSER_MODEL", ".elser_model_2")
2324
POISON_MESSAGE = "~~~END~~~"
2425
SESSION_ID_TAG = "[SESSION_ID]"
2526
SOURCE_TAG = "[SOURCE]"
@@ -71,7 +72,7 @@ def on_llm_end(self, response, *, run_id, parent_run_id=None, **kwargs):
7172
store = ElasticsearchStore(
7273
es_connection=elasticsearch_client,
7374
index_name=INDEX,
74-
strategy=ElasticsearchStore.SparseVectorRetrievalStrategy(),
75+
strategy=ElasticsearchStore.SparseVectorRetrievalStrategy(model_id=ELSER_MODEL),
7576
)
7677

7778
general_system_template = """

example-apps/chatbot-rag-app/data/index-data.py renamed to example-apps/chatbot-rag-app/data/index_data.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
from elasticsearch import Elasticsearch
1+
from elasticsearch import Elasticsearch, NotFoundError
22
from langchain.vectorstores import ElasticsearchStore
33
from langchain.document_loaders import JSONLoader
44
from langchain.text_splitter import CharacterTextSplitter
55
from dotenv import load_dotenv
66
import os
7+
import time
78

89
load_dotenv()
910

@@ -14,12 +15,34 @@
1415
ELASTIC_CLOUD_ID = os.getenv("ELASTIC_CLOUD_ID")
1516
ELASTIC_USERNAME = os.getenv("ELASTIC_USERNAME")
1617
ELASTIC_PASSWORD = os.getenv("ELASTIC_PASSWORD")
18+
ELSER_MODEL = os.getenv("ELSER_MODEL", ".elser_model_2")
1719

1820
elasticsearch_client = Elasticsearch(
1921
cloud_id=ELASTIC_CLOUD_ID, basic_auth=(ELASTIC_USERNAME, ELASTIC_PASSWORD)
2022
)
2123

2224

25+
def install_elser():
26+
try:
27+
elasticsearch_client.ml.get_trained_models(model_id=ELSER_MODEL)
28+
print(f"\"{ELSER_MODEL}\" model is available")
29+
except NotFoundError:
30+
print(f"\"{ELSER_MODEL}\" model not available, downloading it now")
31+
elasticsearch_client.ml.put_trained_model(model_id=ELSER_MODEL,
32+
input={"field_names": ["text_field"]})
33+
while True:
34+
status = elasticsearch_client.ml.get_trained_models(model_id=ELSER_MODEL,
35+
include="definition_status")
36+
if status["trained_model_configs"][0]["fully_defined"]:
37+
# model is ready
38+
break
39+
time.sleep(1)
40+
41+
print("Model downloaded, starting deployment")
42+
elasticsearch_client.ml.start_trained_model_deployment(model_id=ELSER_MODEL,
43+
wait_for="fully_allocated")
44+
45+
2346
# Metadata extraction function
2447
def metadata_func(record: dict, metadata: dict) -> dict:
2548
metadata["name"] = record.get("name")
@@ -31,7 +54,9 @@ def metadata_func(record: dict, metadata: dict) -> dict:
3154
return metadata
3255

3356

34-
if __name__ == "__main__":
57+
def main():
58+
install_elser()
59+
3560
print(f"Loading data from ${FILE}")
3661

3762
loader = JSONLoader(
@@ -54,9 +79,15 @@ def metadata_func(record: dict, metadata: dict) -> dict:
5479
f"Creating Elasticsearch sparse vector store in Elastic Cloud: {ELASTIC_CLOUD_ID}"
5580
)
5681

82+
elasticsearch_client.indices.delete(index=INDEX, ignore_unavailable=True)
83+
5784
ElasticsearchStore.from_documents(
5885
docs,
5986
es_connection=elasticsearch_client,
6087
index_name=INDEX,
61-
strategy=ElasticsearchStore.SparseVectorRetrievalStrategy(),
88+
strategy=ElasticsearchStore.SparseVectorRetrievalStrategy(model_id=ELSER_MODEL),
6289
)
90+
91+
92+
if __name__ == "__main__":
93+
main()
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Make a copy of this file with the name .env and assign values to variables
2+
3+
# Your Elastic Cloud credentials
4+
ELASTIC_USERNAME=elastic
5+
ELASTIC_CLOUD_ID=
6+
ELASTIC_PASSWORD=
7+
8+
# The name of the Elasticsearch indexes
9+
ES_INDEX=workplace-app-docs
10+
ES_INDEX_CHAT_HISTORY=workplace-app-docs-chat-history
11+
12+
# Uncomment and complete if you want to use OpenAI
13+
# LLM_TYPE=openai
14+
# OPENAI_API_KEY=
15+
16+
# Uncomment and complete if you want to use Azure OpenAI
17+
# LLM_TYPE=azure
18+
# OPENAI_VERSION=
19+
# OPENAI_BASE_URL=
20+
# OPENAI_API_KEY=
21+
# OPENAI_ENGINE=
22+
23+
# Uncomment and complete if you want to use Bedrock LLM
24+
# LLM_TYPE=bedrock
25+
# AWS_ACCESS_KEY=
26+
# AWS_SECRET_KEY=
27+
# AWS_REGION=
28+
# AWS_MODEL_ID=
29+
30+
# Uncomment and complete if you want to use Vertex AI
31+
# LLM_TYPE=vertex
32+
# VERTEX_PROJECT_ID=
33+
# VERTEX_REGION=
34+
# GOOGLE_APPLICATION_CREDENTIALS=

example-apps/chatbot-rag-app/requirements.txt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,19 @@ exceptiongroup==1.1.3
1818
Flask==2.3.3
1919
Flask-Cors==4.0.0
2020
frozenlist==1.4.0
21+
google-api-core==2.14.0
2122
google-auth==2.23.2
23+
google-cloud-aiplatform==1.35.0
24+
google-cloud-bigquery==3.13.0
25+
google-cloud-core==2.3.3
26+
google-cloud-resource-manager==1.10.4
27+
google-cloud-storage==2.11.0
28+
google-crc32c==1.5.0
29+
google-resumable-media==2.6.0
30+
googleapis-common-protos==1.61.0
31+
grpc-google-iam-v1==0.12.7
32+
grpcio==1.59.3
33+
grpcio-status==1.59.3
2234
idna==3.4
2335
importlib-metadata==6.8.0
2436
itsdangerous==2.1.2
@@ -28,6 +40,7 @@ jq==1.4.1
2840
jsonpatch==1.33
2941
jsonpointer==2.4
3042
langchain==0.0.315
43+
langsmith==0.0.65
3144
MarkupSafe==2.1.3
3245
marshmallow==3.20.1
3346
multidict==6.0.4
@@ -36,15 +49,19 @@ numexpr==2.8.5
3649
numpy==1.25.2
3750
openai==0.27.9
3851
packaging==23.1
52+
proto-plus==1.22.3
53+
protobuf==4.25.1
3954
pyasn1==0.5.0
4055
pyasn1-modules==0.3.0
4156
pydantic==2.3.0
4257
pydantic_core==2.6.3
4358
python-dateutil==2.8.2
59+
python-dotenv==1.0.0
4460
PyYAML==6.0.1
4561
requests==2.31.0
4662
rsa==4.9
4763
s3transfer==0.7.0
64+
shapely==2.0.2
4865
six==1.16.0
4966
sniffio==1.3.0
5067
SQLAlchemy==2.0.20
@@ -56,4 +73,3 @@ urllib3==1.26.16
5673
Werkzeug==2.3.7
5774
yarl==1.9.2
5875
zipp==3.17.0
59-
google-cloud-aiplatform==1.35.0

0 commit comments

Comments
 (0)