Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Cole <[email protected]>
  • Loading branch information
codefromthecrypt committed Feb 20, 2025
1 parent dd4a381 commit 7f82332
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 47 deletions.
54 changes: 34 additions & 20 deletions example-apps/chatbot-rag-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,34 +45,48 @@ and configure its templated connection settings:

## Running the App

This application contains two services:
* create-index: Installs ELSER and ingests data into elasticsearch
* api-frontend: Hosts the chatbot-rag-app application on http://localhost:4000

There are two ways to run the app: via Docker or locally. Docker is advised for
ease while locally is advised if you are making changes to the application.

### Run with docker

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

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

#### Create your Elasticsearch index

First, ingest the data into elasticsearch:
```bash
docker compose run -T --rm --pull always create-index
```

*Note*: This may take several minutes to complete

#### Run the application

Now, run the app, which listens on http://localhost:4000
```bash
docker compose up --pull always --force-recreate
docker compose run --rm --pull always api-frontend
```

*Note*: First time creating the index can fail on timeout. Wait a few minutes
and retry.
#### Cleanup when finished

Clean up when finished, like this:
When you are done, clean up the services above like this:

```bash
docker compose down
```

### Run locally
### Run with Python

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

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

#### Configure your python environment
#### Configure your Python environment

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

#### Run the ingest command
#### Create your Elasticsearch index

First, ingest the data into elasticsearch:
```bash
FLASK_APP=api/app.py dotenv run -- flask create-index
dotenv run -- flask create-index
```

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

#### Run the app
#### Run the application

Now, run the app, which listens on http://localhost:4000
```bash
Expand Down Expand Up @@ -185,13 +198,14 @@ passages. Modify this script to index your own data.

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

### Building from source with docker
### Running from source with Docker

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

For example, to run the create-index service from source:
```bash
docker compose up --build --force-recreate
docker compose run --rm --build create-index
```

---
Expand Down
45 changes: 30 additions & 15 deletions example-apps/chatbot-rag-app/data/index_data.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import json
import os
from sys import stdout
import time
from halo import Halo
from warnings import warn

from elasticsearch import (
Expand Down Expand Up @@ -135,30 +137,43 @@ def await_ml_tasks(max_timeout=600, interval=5):
TimeoutError: If the timeout is reached and machine learning tasks are still running.
"""
start_time = time.time()
ml_tasks = get_ml_tasks()
if not ml_tasks:
return # likely a lost race on tasks

spinner = Halo(text="Awaiting ML tasks", spinner="dots")
if stdout.isatty():
spinner.start()
else:
print(f"Awaiting {len(ml_tasks)} ML tasks")

tasks = [] # Initialize tasks list
previous_task_count = 0 # Track the previous number of tasks
while time.time() - start_time < max_timeout:
tasks = []
resp = es.tasks.list(detailed=True, actions=["cluster:monitor/xpack/ml/*"])
for node_id, node_info in resp["nodes"].items():
node_tasks = node_info.get("tasks", {})
for task_id, task_info in node_tasks.items():
tasks.append(task_info["action"])
if not tasks:
ml_tasks = get_ml_tasks()
if len(ml_tasks) == 0:
break
current_task_count = len(tasks)
if current_task_count != previous_task_count:
warn(f"Awaiting {current_task_count} ML tasks")
previous_task_count = current_task_count
time.sleep(interval)

if tasks:
if stdout.isatty():
spinner.stop()
else:
print(f"ML tasks complete")

if ml_tasks:
raise TimeoutError(
f"Timeout reached. ML tasks are still running: {', '.join(tasks)}"
f"Timeout reached. ML tasks are still running: {', '.join(ml_tasks)}"
)


def get_ml_tasks():
"""Return a list of ML task actions from the ES tasks API."""
tasks = []
resp = es.tasks.list(detailed=True, actions=["cluster:monitor/xpack/ml/*"])
for node_info in resp["nodes"].values():
for task_info in node_info.get("tasks", {}).values():
tasks.append(task_info["action"])
return tasks


# Unless we run through flask, we can miss critical settings or telemetry signals.
if __name__ == "__main__":
main()
12 changes: 7 additions & 5 deletions example-apps/chatbot-rag-app/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
name: chatbot-rag-app

services:
ingest-data:
create-index:
image: ghcr.io/elastic/elasticsearch-labs/chatbot-rag-app
build:
context: .
container_name: ingest-data
container_name: create-index
restart: 'no' # no need to re-ingest on successive runs
environment:
FLASK_APP: api/app.py
env_file:
- .env
# Add settings that allow `docker compose run` to use tty and accept Ctrl+C
stdin_open: true
command: flask create-index
volumes:
# VertexAI uses a file for GOOGLE_APPLICATION_CREDENTIALS, not an API key
Expand All @@ -20,14 +20,16 @@ services:

api-frontend:
depends_on:
ingest-data:
create-index:
condition: service_completed_successfully
container_name: api-frontend
image: ghcr.io/elastic/elasticsearch-labs/chatbot-rag-app
build:
context: .
env_file:
- .env
# Add settings that allow `docker compose run` to use tty and accept Ctrl+C
stdin_open: true
volumes:
# VertexAI uses a file for GOOGLE_APPLICATION_CREDENTIALS, not an API key
- ${HOME}/.config/gcloud:/root/.config/gcloud
Expand Down
5 changes: 5 additions & 0 deletions example-apps/chatbot-rag-app/env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Make a copy of this file with the name .env and assign values to variables

# Location of the application routes
FLASK_APP=api/app.py
# Ensure print statements appear as they happen
PYTHONUNBUFFERED=1

# How you connect to Elasticsearch: change details to your instance
ELASTICSEARCH_URL=http://localhost:9200
ELASTICSEARCH_USER=elastic
Expand Down
1 change: 1 addition & 0 deletions example-apps/chatbot-rag-app/requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ langchain-elasticsearch
tiktoken
flask
flask-cors
halo

# LLM dependencies
langchain-openai
Expand Down
27 changes: 20 additions & 7 deletions example-apps/chatbot-rag-app/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ attrs==25.1.0
# via aiohttp
blinker==1.9.0
# via flask
boto3==1.36.23
boto3==1.36.24
# via
# langchain-aws
# langtrace-python-sdk
botocore==1.36.23
botocore==1.36.24
# via
# boto3
# s3transfer
Expand All @@ -46,7 +46,10 @@ click==8.1.8
cohere==5.13.12
# via langchain-cohere
colorama==0.4.6
# via langtrace-python-sdk
# via
# halo
# langtrace-python-sdk
# log-symbols
dataclasses-json==0.6.7
# via langchain-community
deprecated==1.2.18
Expand Down Expand Up @@ -144,6 +147,8 @@ grpcio-status==1.70.0
# via google-api-core
h11==0.14.0
# via httpcore
halo==0.0.31
# via -r requirements.in
httpcore==1.0.7
# via httpx
httpx==0.28.1
Expand All @@ -159,7 +164,7 @@ httpx-sse==0.4.0
# langchain-community
# langchain-google-vertexai
# langchain-mistralai
huggingface-hub==0.28.1
huggingface-hub==0.29.0
# via
# tokenizers
# transformers
Expand Down Expand Up @@ -193,9 +198,9 @@ langchain-aws==0.2.13
# via -r requirements.in
langchain-cohere==0.4.2
# via -r requirements.in
langchain-community==0.3.17
langchain-community==0.3.18
# via langchain-cohere
langchain-core==0.3.36
langchain-core==0.3.37
# via
# langchain
# langchain-aws
Expand Down Expand Up @@ -223,6 +228,8 @@ langsmith==0.3.8
# langchain-core
langtrace-python-sdk==3.6.2
# via -r requirements.in
log-symbols==0.0.14
# via halo
markupsafe==3.0.2
# via
# jinja2
Expand Down Expand Up @@ -414,11 +421,15 @@ shapely==2.0.7
simsimd==6.2.1
# via elasticsearch
six==1.17.0
# via python-dateutil
# via
# halo
# python-dateutil
sniffio==1.3.1
# via
# anyio
# openai
spinners==0.0.24
# via halo
sqlalchemy==2.0.38
# via
# langchain
Expand All @@ -429,6 +440,8 @@ tenacity==9.0.0
# langchain
# langchain-community
# langchain-core
termcolor==2.5.0
# via halo
tiktoken==0.9.0
# via
# -r requirements.in
Expand Down

0 comments on commit 7f82332

Please sign in to comment.