Skip to content

Commit ba5f112

Browse files
chatbot-rag-app: updates ENV, docker setup and README (#364)
Signed-off-by: Adrian Cole <[email protected]>
1 parent d4d492e commit ba5f112

File tree

9 files changed

+196
-73
lines changed

9 files changed

+196
-73
lines changed

example-apps/chatbot-rag-app/.flaskenv

Lines changed: 0 additions & 4 deletions
This file was deleted.

example-apps/chatbot-rag-app/Dockerfile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:20-alpine AS build-step
1+
FROM node:22-alpine AS build-step
22
WORKDIR /app
33
ENV PATH=/node_modules/.bin:$PATH
44
COPY frontend ./frontend
@@ -28,7 +28,4 @@ COPY api ./api
2828
COPY data ./data
2929

3030
EXPOSE 4000
31-
# The only thing different from running local is that in docker we need to
32-
# listen on all IPs, not just localhost.
33-
ENV FLASK_RUN_HOST=0.0.0.0
34-
CMD [ "flask", "run"]
31+
CMD [ "python", "api/app.py"]

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

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,27 @@ use-cases. Visit the [Install Elasticsearch](https://www.elastic.co/search-labs/
2626

2727
Once you decided your approach, edit your `.env` file accordingly.
2828

29-
### Elasticsearch index and chat_history index
29+
### Running your own Elastic Stack with Docker
3030

31-
By default, the app will use the `workplace-app-docs` index and the chat
32-
history index will be `workplace-app-docs-chat-history`. If you want to change
33-
these, edit `ES_INDEX` and `ES_INDEX_CHAT_HISTORY` entries in your `.env` file.
31+
If you'd like to start Elastic locally, you can use the provided
32+
[docker-compose-elastic.yml](docker-compose-elastic.yml) file. This starts
33+
Elasticsearch, Kibana, and APM Server and only requires Docker installed.
34+
35+
Use docker compose to run Elastic stack in the background:
36+
37+
```bash
38+
docker compose -f docker-compose-elastic.yml up --force-recreate -d
39+
```
40+
41+
Then, you can view Kibana at http://localhost:5601/app/home#/
42+
43+
If asked for a username and password, use username: elastic and password: elastic.
44+
45+
Clean up when finished, like this:
46+
47+
```bash
48+
docker compose -f docker-compose-elastic.yml down
49+
```
3450

3551
## Connecting to LLM
3652

@@ -67,6 +83,12 @@ docker compose up --build --force-recreate
6783
*Note*: First time creating the index can fail on timeout. Wait a few minutes
6884
and retry.
6985

86+
Clean up when finished, like this:
87+
88+
```bash
89+
docker compose down
90+
```
91+
7092
### Run locally
7193

7294
If you want to run this example with Python and Node.js, you need to do a few
@@ -95,23 +117,16 @@ correct packages installed:
95117
```bash
96118
python3 -m venv .venv
97119
source .venv/bin/activate
98-
# install dev requirements for pip-compile and dotenv
99-
pip install pip-tools "python-dotenv[cli]"
100-
pip-compile
120+
# Install dotenv which is a portable way to load environment variables.
121+
pip install "python-dotenv[cli]"
101122
pip install -r requirements.txt
102123
```
103124

104125
#### Run the ingest command
105126

106127
First, ingest the data into elasticsearch:
107128
```bash
108-
$ dotenv run -- flask create-index
109-
".elser_model_2" model not available, downloading it now
110-
Model downloaded, starting deployment
111-
Loading data from ./data/data.json
112-
Loaded 15 documents
113-
Split 15 documents into 26 chunks
114-
Creating Elasticsearch sparse vector store in http://localhost:9200
129+
FLASK_APP=api/app.py dotenv run -- flask create-index
115130
```
116131

117132
*Note*: First time creating the index can fail on timeout. Wait a few minutes
@@ -121,12 +136,33 @@ and retry.
121136

122137
Now, run the app, which listens on http://localhost:4000
123138
```bash
124-
$ dotenv run -- flask run
125-
* Serving Flask app 'api/app.py'
126-
* Debug mode: off
139+
dotenv run -- python api/app.py
127140
```
128141

129-
## Customizing the app
142+
## Advanced
143+
144+
### Updating package versions
145+
146+
To update package versions, recreate [requirements.txt](requirements.txt) and
147+
reinstall like this. Once checked in, any commands above will use updates.
148+
149+
```bash
150+
rm -rf .venv
151+
python3 -m venv .venv
152+
source .venv/bin/activate
153+
# Install dev requirements for pip-compile
154+
pip install pip-tools
155+
# Recreate requirements.txt
156+
pip-compile
157+
# Install main dependencies
158+
pip install -r requirements.txt
159+
```
160+
161+
### Elasticsearch index and chat_history index
162+
163+
By default, the app will use the `workplace-app-docs` index and the chat
164+
history index will be `workplace-app-docs-chat-history`. If you want to change
165+
these, edit `ES_INDEX` and `ES_INDEX_CHAT_HISTORY` entries in your `.env` file.
130166

131167
### Indexing your own data
132168

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,5 @@ def create_index():
3737
index_data.main()
3838

3939

40-
# Unless we run through flask, we can miss critical settings or telemetry signals.
4140
if __name__ == "__main__":
42-
raise RuntimeError("Run via the parent directory: 'flask run'")
41+
app.run(host="0.0.0.0", port=4000, debug=False)

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

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,60 +11,47 @@
1111

1212

1313
def init_openai_chat(temperature):
14+
# Include streaming usage as this allows recording of LLM metrics
1415
return ChatOpenAI(
15-
model=os.getenv("CHAT_MODEL"), streaming=True, temperature=temperature
16+
model=os.getenv("CHAT_MODEL"),
17+
streaming=True,
18+
temperature=temperature,
19+
model_kwargs={"stream_options": {"include_usage": True}},
1620
)
1721

1822

1923
def init_vertex_chat(temperature):
20-
VERTEX_PROJECT_ID = os.getenv("VERTEX_PROJECT_ID")
21-
VERTEX_REGION = os.getenv("VERTEX_REGION", "us-central1")
22-
vertexai.init(project=VERTEX_PROJECT_ID, location=VERTEX_REGION)
23-
return ChatVertexAI(streaming=True, temperature=temperature)
24+
return ChatVertexAI(
25+
model_name=os.getenv("CHAT_MODEL"), streaming=True, temperature=temperature
26+
)
2427

2528

2629
def init_azure_chat(temperature):
30+
# Include streaming usage as this allows recording of LLM metrics
2731
return AzureChatOpenAI(
28-
model=os.getenv("CHAT_DEPLOYMENT"), streaming=True, temperature=temperature
32+
model=os.getenv("CHAT_DEPLOYMENT"),
33+
streaming=True,
34+
temperature=temperature,
35+
model_kwargs={"stream_options": {"include_usage": True}},
2936
)
3037

3138

3239
def init_bedrock(temperature):
33-
AWS_ACCESS_KEY = os.getenv("AWS_ACCESS_KEY")
34-
AWS_SECRET_KEY = os.getenv("AWS_SECRET_KEY")
35-
AWS_REGION = os.getenv("AWS_REGION")
36-
AWS_MODEL_ID = os.getenv("AWS_MODEL_ID", "anthropic.claude-v2")
3740
return ChatBedrock(
38-
region_name=AWS_REGION,
39-
aws_access_key_id=AWS_ACCESS_KEY,
40-
aws_secret_access_key=AWS_SECRET_KEY,
41-
model_id=AWS_MODEL_ID,
41+
model_id=os.getenv("CHAT_MODEL"),
4242
streaming=True,
4343
model_kwargs={"temperature": temperature},
4444
)
4545

4646

4747
def init_mistral_chat(temperature):
48-
MISTRAL_API_ENDPOINT = os.getenv("MISTRAL_API_ENDPOINT")
49-
MISTRAL_API_KEY = os.getenv("MISTRAL_API_KEY")
50-
MISTRAL_MODEL = os.getenv("MISTRAL_MODEL", "Mistral-large")
51-
kwargs = {
52-
"mistral_api_key": MISTRAL_API_KEY,
53-
"temperature": temperature,
54-
}
55-
if MISTRAL_API_ENDPOINT:
56-
kwargs["endpoint"] = MISTRAL_API_ENDPOINT
57-
if MISTRAL_MODEL:
58-
kwargs["model"] = MISTRAL_MODEL
59-
return ChatMistralAI(**kwargs)
48+
return ChatMistralAI(
49+
model=os.getenv("CHAT_MODEL"), streaming=True, temperature=temperature
50+
)
6051

6152

6253
def init_cohere_chat(temperature):
63-
COHERE_API_KEY = os.getenv("COHERE_API_KEY")
64-
COHERE_MODEL = os.getenv("COHERE_MODEL")
65-
return ChatCohere(
66-
cohere_api_key=COHERE_API_KEY, model=COHERE_MODEL, temperature=temperature
67-
)
54+
return ChatCohere(model=os.getenv("CHAT_MODEL"), temperature=temperature)
6855

6956

7057
MAP_LLM_TYPE_TO_CHAT_MODEL = {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: elastic-stack
2+
3+
services:
4+
elasticsearch:
5+
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
6+
container_name: elasticsearch
7+
ports:
8+
- 9200:9200
9+
environment:
10+
- node.name=elasticsearch
11+
- cluster.name=docker-cluster
12+
- discovery.type=single-node
13+
- ELASTIC_PASSWORD=elastic
14+
- bootstrap.memory_lock=true
15+
- xpack.security.enabled=true
16+
- xpack.security.http.ssl.enabled=false
17+
- xpack.security.transport.ssl.enabled=false
18+
- xpack.license.self_generated.type=trial
19+
- ES_JAVA_OPTS=-Xmx8g
20+
ulimits:
21+
memlock:
22+
soft: -1
23+
hard: -1
24+
healthcheck:
25+
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=500ms"]
26+
retries: 300
27+
interval: 1s
28+
29+
elasticsearch_settings:
30+
depends_on:
31+
elasticsearch:
32+
condition: service_healthy
33+
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
34+
container_name: elasticsearch_settings
35+
restart: 'no'
36+
command: >
37+
bash -c '
38+
# gen-ai assistants in kibana save state in a way that requires security to be enabled, so we need to create
39+
# a kibana system user before starting it.
40+
echo "Setup the kibana_system password";
41+
until curl -s -u "elastic:elastic" -X POST http://elasticsearch:9200/_security/user/kibana_system/_password -d "{\"password\":\"elastic\"}" -H "Content-Type: application/json" | grep -q "^{}"; do sleep 5; done;
42+
'
43+
44+
kibana:
45+
image: docker.elastic.co/kibana/kibana:8.17.0
46+
container_name: kibana
47+
depends_on:
48+
elasticsearch_settings:
49+
condition: service_completed_successfully
50+
ports:
51+
- 5601:5601
52+
environment:
53+
- SERVERNAME=kibana
54+
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
55+
- ELASTICSEARCH_USERNAME=kibana_system
56+
- ELASTICSEARCH_PASSWORD=elastic
57+
# Non-default settings from here:
58+
# https://github.com/elastic/apm-server/blob/main/testing/docker/kibana/kibana.yml
59+
- MONITORING_UI_CONTAINER_ELASTICSEARCH_ENABLED=true
60+
- XPACK_SECURITY_ENCRYPTIONKEY=fhjskloppd678ehkdfdlliverpoolfcr
61+
- XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=fhjskloppd678ehkdfdlliverpoolfcr
62+
- SERVER_PUBLICBASEURL=http://127.0.0.1:5601
63+
healthcheck:
64+
test: ["CMD-SHELL", "curl -s http://localhost:5601/api/status | grep -q 'All services are available'"]
65+
retries: 300
66+
interval: 1s
67+
68+
apm-server:
69+
image: docker.elastic.co/apm/apm-server:8.17.0
70+
container_name: apm-server
71+
depends_on:
72+
elasticsearch:
73+
condition: service_healthy
74+
command: >
75+
apm-server
76+
-E apm-server.kibana.enabled=true
77+
-E apm-server.kibana.host=http://kibana:5601
78+
-E apm-server.kibana.username=elastic
79+
-E apm-server.kibana.password=elastic
80+
-E output.elasticsearch.hosts=["http://elasticsearch:9200"]
81+
-E output.elasticsearch.username=elastic
82+
-E output.elasticsearch.password=elastic
83+
cap_add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"]
84+
cap_drop: ["ALL"]
85+
ports:
86+
- 8200:8200
87+
healthcheck:
88+
test: ["CMD-SHELL", "bash -c 'echo -n > /dev/tcp/127.0.0.1/8200'"]
89+
retries: 300
90+
interval: 1s
91+
Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1+
name: chatbot-rag-app
2+
13
services:
24
ingest-data:
35
build:
46
context: .
57
container_name: ingest-data
68
restart: 'no'
9+
environment:
10+
# host.docker.internal means connect to the host machine, e.g. your laptop
11+
ELASTICSEARCH_URL: "http://host.docker.internal:9200"
12+
FLASK_APP: api/app.py
713
env_file:
814
- .env
9-
- .flaskenv
1015
command: flask create-index
16+
extra_hosts:
17+
- "host.docker.internal:host-gateway"
1118

1219
api-frontend:
1320
depends_on:
@@ -16,8 +23,12 @@ services:
1623
container_name: api-frontend
1724
build:
1825
context: .
26+
environment:
27+
# host.docker.internal means connect to the host machine, e.g. your laptop
28+
ELASTICSEARCH_URL: "http://host.docker.internal:9200"
1929
env_file:
2030
- .env
21-
- .flaskenv
2231
ports:
2332
- "4000:4000"
33+
extra_hosts:
34+
- "host.docker.internal:host-gateway"

example-apps/chatbot-rag-app/env.example

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,31 @@ ES_INDEX_CHAT_HISTORY=workplace-app-docs-chat-history
2828

2929
# Uncomment and complete if you want to use Bedrock LLM
3030
# LLM_TYPE=bedrock
31-
# AWS_ACCESS_KEY=
32-
# AWS_SECRET_KEY=
33-
# AWS_REGION=
34-
# AWS_MODEL_ID=
31+
# AWS_ACCESS_KEY_ID=
32+
# AWS_SECRET_ACCESS_KEY=
33+
# AWS_DEFAULT_REGION=
34+
# CHAT_MODEL=anthropic.claude-3-5-sonnet-20240620-v1:0
3535

3636
# Uncomment and complete if you want to use Vertex AI
3737
# LLM_TYPE=vertex
38-
# VERTEX_PROJECT_ID=
39-
# VERTEX_REGION=
38+
## Project that has the service "aiplatform.googleapis.com" enabled
39+
# GOOGLE_CLOUD_PROJECT=
40+
# GOOGLE_CLOUD_REGION=
41+
# CHAT_MODEL=gemini-1.5-flash-002
42+
## Needed if you haven't run `gcloud auth application-default login`
4043
# GOOGLE_APPLICATION_CREDENTIALS=
4144

4245
# Uncomment and complete if you want to use Mistral AI
4346
# LLM_TYPE=mistral
47+
## Key in https://console.mistral.ai/api-keys/
4448
# MISTRAL_API_KEY=
45-
# MISTRAL_API_ENDPOINT=
46-
# MISTRAL_MODEL=
49+
## 'API Endpoints' from https://docs.mistral.ai/getting-started/models/models_overview/
50+
# CHAT_MODEL=open-mistral-nemo
51+
## Only set this if not using the default Mistral base URL
52+
# MISTRAL_BASE_URL=
4753

4854
# Uncomment and complete if you want to use Cohere
4955
# LLM_TYPE=cohere
56+
## Key in https://dashboard.cohere.com/api-keys
5057
# COHERE_API_KEY=
51-
# COHERE_MODEL=
58+
# CHAT_MODEL=command-r7b-12-2024

0 commit comments

Comments
 (0)