Skip to content

Commit 2580c00

Browse files
chatbot-rag-app: recover from timeouts on first use of ELSER (#397)
Signed-off-by: Adrian Cole <[email protected]>
1 parent ce9eeb2 commit 2580c00

File tree

10 files changed

+266
-127
lines changed

10 files changed

+266
-127
lines changed

Diff for: .github/workflows/docker-chatbot-rag-app.yml

+18-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ on:
1212
branches:
1313
- main
1414
paths:
15-
# Verify changes to the Dockerfile on PRs
15+
# Verify changes to the Dockerfile on PRs, tainted when we update ES.
16+
- docker/docker-compose-elastic.yml
17+
- example-apps/chatbot-rag-app/docker-compose.yml
1618
- example-apps/chatbot-rag-app/Dockerfile
1719
- .github/workflows/docker-chatbot-rag-app.yml
1820
- '!**/*.md'
@@ -42,13 +44,27 @@ jobs:
4244
registry: ghcr.io
4345
username: ${{ github.actor }}
4446
password: ${{ secrets.GITHUB_TOKEN }}
47+
# This builds the image and pushes its digest if a multi-architecture
48+
# image will be made later (event_name == 'push'). If PR, the image is
49+
# loaded into docker for testing.
4550
- uses: docker/build-push-action@v6
4651
id: build
4752
with:
4853
context: example-apps/chatbot-rag-app
49-
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=${{ github.event_name == 'push' && 'true' || 'false' }}
54+
outputs: type=${{ github.event_name == 'pull_request' && 'docker' || 'image' }},name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=${{ github.event_name == 'push' && 'true' || 'false' }}
5055
cache-from: type=gha
5156
cache-to: type=gha,mode=max
57+
- name: start elasticsearch
58+
if: github.event_name == 'pull_request'
59+
run: docker compose -f docker/docker-compose-elastic.yml up --quiet-pull -d --wait --wait-timeout 120 elasticsearch
60+
- name: test image
61+
if: github.event_name == 'pull_request'
62+
working-directory: example-apps/chatbot-rag-app
63+
run: | # This tests ELSER is working, which doesn't require an LLM.
64+
cp env.example .env
65+
# same as `docker compose run --rm -T create-index`, except pull never
66+
docker run --rm --name create-index --env-file .env --pull never \
67+
--add-host "localhost:host-gateway" ${{ env.IMAGE }} flask create-index
5268
- name: export digest
5369
if: github.event_name == 'push'
5470
run: |

Diff for: docker/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ wget https://raw.githubusercontent.com/elastic/elasticsearch-labs/refs/heads/mai
1212
Use docker compose to run Elastic stack in the background:
1313

1414
```bash
15-
docker compose -f docker-compose-elastic.yml up --force-recreate -d
15+
docker compose -f docker-compose-elastic.yml up --force-recreate --wait -d
1616
```
1717

1818
Then, you can view Kibana at http://localhost:5601/app/home#/

Diff for: docker/docker-compose-elastic.yml

+15-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: elastic-stack
22

33
services:
44
elasticsearch:
5-
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
5+
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.2
66
container_name: elasticsearch
77
ports:
88
- 9200:9200
@@ -16,21 +16,29 @@ services:
1616
- xpack.security.http.ssl.enabled=false
1717
- xpack.security.transport.ssl.enabled=false
1818
- xpack.license.self_generated.type=trial
19-
- ES_JAVA_OPTS=-Xmx8g
19+
# Note that ELSER is recommended to have 2GB, but it is JNI (PyTorch).
20+
# So, ELSER's memory is in addition to the heap and other overhead.
21+
- ES_JAVA_OPTS=-Xms2g -Xmx2g
2022
ulimits:
2123
memlock:
2224
soft: -1
2325
hard: -1
2426
healthcheck:
25-
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=500ms"]
26-
retries: 300
27+
test: # readiness probe taken from kbn-health-gateway-server script
28+
[
29+
"CMD-SHELL",
30+
"curl -s http://localhost:9200 | grep -q 'missing authentication credentials'",
31+
]
32+
start_period: 10s
2733
interval: 1s
34+
timeout: 10s
35+
retries: 120
2836

2937
elasticsearch_settings:
3038
depends_on:
3139
elasticsearch:
3240
condition: service_healthy
33-
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
41+
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.2
3442
container_name: elasticsearch_settings
3543
restart: 'no'
3644
command: >
@@ -42,7 +50,7 @@ services:
4250
'
4351
4452
kibana:
45-
image: docker.elastic.co/kibana/kibana:8.17.0
53+
image: docker.elastic.co/kibana/kibana:8.17.2
4654
container_name: kibana
4755
depends_on:
4856
elasticsearch_settings:
@@ -66,7 +74,7 @@ services:
6674
interval: 1s
6775

6876
apm-server:
69-
image: docker.elastic.co/apm/apm-server:8.17.0
77+
image: docker.elastic.co/apm/apm-server:8.17.2
7078
container_name: apm-server
7179
depends_on:
7280
elasticsearch:

Diff for: example-apps/chatbot-rag-app/README.md

+18-17
Original file line numberDiff line numberDiff line change
@@ -45,34 +45,36 @@ and configure its templated connection settings:
4545

4646
## Running the App
4747

48+
This application contains two services:
49+
* create-index: Installs ELSER and ingests data into elasticsearch
50+
* api-frontend: Hosts the chatbot-rag-app application on http://localhost:4000
51+
4852
There are two ways to run the app: via Docker or locally. Docker is advised for
4953
ease while locally is advised if you are making changes to the application.
5054

5155
### Run with docker
5256

53-
Docker compose is the easiest way, as you get one-step to:
54-
* ingest data into elasticsearch
55-
* run the app, which listens on http://localhost:4000
57+
Docker compose is the easiest way to get started, as you don't need to have a
58+
working Python environment.
5659

5760
**Double-check you have a `.env` file with all your variables set first!**
5861

5962
```bash
6063
docker compose up --pull always --force-recreate
6164
```
6265

63-
*Note*: First time creating the index can fail on timeout. Wait a few minutes
64-
and retry.
66+
*Note*: The first run may take several minutes to become available.
6567

6668
Clean up when finished, like this:
6769

6870
```bash
6971
docker compose down
7072
```
7173

72-
### Run locally
74+
### Run with Python
7375

74-
If you want to run this example with Python and Node.js, you need to do a few
75-
things listed in the [Dockerfile](Dockerfile). The below uses the same
76+
If you want to run this example with Python, you need to do a few things listed
77+
in the [Dockerfile](Dockerfile) to build it first. The below uses the same
7678
production mode as used in Docker to avoid problems in debug mode.
7779

7880
**Double-check you have a `.env` file with all your variables set first!**
@@ -89,7 +91,7 @@ nvm use --lts
8991
(cd frontend; yarn install; REACT_APP_API_HOST=/api yarn build)
9092
```
9193

92-
#### Configure your python environment
94+
#### Configure your Python environment
9395

9496
Before we can run the app, we need a working Python environment with the
9597
correct packages installed:
@@ -102,17 +104,16 @@ pip install "python-dotenv[cli]"
102104
pip install -r requirements.txt
103105
```
104106

105-
#### Run the ingest command
107+
#### Create your Elasticsearch index
106108

107109
First, ingest the data into elasticsearch:
108110
```bash
109-
FLASK_APP=api/app.py dotenv run -- flask create-index
111+
dotenv run -- flask create-index
110112
```
111113

112-
*Note*: First time creating the index can fail on timeout. Wait a few minutes
113-
and retry.
114+
*Note*: This may take several minutes to complete
114115

115-
#### Run the app
116+
#### Run the application
116117

117118
Now, run the app, which listens on http://localhost:4000
118119
```bash
@@ -185,10 +186,10 @@ passages. Modify this script to index your own data.
185186

186187
See [Langchain documentation][loader-docs] for more ways to load documents.
187188

188-
### Building from source with docker
189+
### Running from source with Docker
189190

190-
To build the app from source instead of using published images, pass the `--build`
191-
flag to Docker Compose.
191+
To build the app from source instead of using published images, pass the
192+
`--build` flag to Docker Compose instead of `--pull always`
192193

193194
```bash
194195
docker compose up --build --force-recreate

Diff for: example-apps/chatbot-rag-app/api/chat.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
get_elasticsearch_chat_message_history,
77
)
88
from flask import current_app, render_template, stream_with_context
9+
from functools import cache
910
from langchain_elasticsearch import (
1011
ElasticsearchStore,
1112
SparseVectorStrategy,
@@ -27,11 +28,16 @@
2728
strategy=SparseVectorStrategy(model_id=ELSER_MODEL),
2829
)
2930

30-
llm = get_llm()
31+
32+
@cache
33+
def get_lazy_llm():
34+
return get_llm()
3135

3236

3337
@stream_with_context
3438
def ask_question(question, session_id):
39+
llm = get_lazy_llm()
40+
3541
yield f"data: {SESSION_ID_TAG} {session_id}\n\n"
3642
current_app.logger.debug("Chat session ID: %s", session_id)
3743

0 commit comments

Comments
 (0)