Skip to content

Commit 3a691b0

Browse files
Split examples, add upload to flask api
1 parent 090d7f2 commit 3a691b0

19 files changed

+586
-67
lines changed

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,32 @@ pip install -r requirements.txt
1818

1919

2020
## What is included?
21-
There are two main example files
22-
- flask_demo.py (localhost:5601)
23-
- `python ./flask_demo.py`
21+
There are two main example folders
22+
- flask (runs on localhost:5601/2)
23+
- `sh launch_app.py `
2424
- creates a simple api that loads the text from the documents folder
25-
- The "/query" endpoint accepts requests that contain a "text" parameter, which is used to query the index
26-
- resturns string response containing the query answer
25+
- the "/query" endpoint accepts requests that contain a "text" parameter, which is used to query the index
26+
- the "/upload" endpoint is a POST endpoint that inserts an attached text file into the index
27+
- the index is managed by a seperate server using locks, since inserting a document is a mutable operation and flask is multithreaded
28+
- I strongly recommend using a tool like [Postman](https://www.postman.com/downloads/) to test the api - there are example screenshots using postman in the `postman_examples` folder
2729

28-
- streamlit_demo.py (localhost:8501)
30+
- streamlit (runs on localhost:8501)
2931
- `streamlit run streamlit_demo.py`
3032
- creates a simple UI using streamlit
3133
- loads text from the documents folder (using `st.cache_resource`, so it only loads once)
3234
- provides an input text-box and a button to run the query
33-
- The string response is displayed after it finishes
34-
- Want to see this example in action? Check it out [here](https://llama-index.streamlit.app/)
35+
- the string response is displayed after it finishes
36+
- want to see this example in action? Check it out [here](https://llama-index.streamlit.app/)
3537

3638

3739
## Docker
38-
Using the local `Dockerfile`, you can run `docker build -t my_tag_name .` to build a python3.11-slim docker image. It ends up being about 980MB.
40+
Each example contains a `Dockerfile`. You can run `docker build -t my_tag_name .` to build a python3.11-slim docker image inside your desired folder. It ends up being about 600MB.
3941

40-
Inside the `Dockerfile`, you can comment the app you want to run and the port you want to expose, based on if you want streamlit or flask.
42+
Inside the `Dockerfile`, certain ports are exposed based on which ports the examples need.
4143

4244
When running the image, be sure to include the -p option to access the proper ports (8501, or 5601).
4345

4446

4547
## Contributing
4648

47-
I welcome any suggestions or PRs! If we start adding more examples, it might be good to refactor to have a folder per example type
48-
49-
49+
I welcome any suggestions or PRs, or more examples!
-40 KB
Binary file not shown.

Dockerfile renamed to flask/Dockerfile

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ COPY . .
66

77
RUN pip install -r requirements.txt && pip cache purge
88

9-
# Streamlit
10-
#CMD ["streamlit", "run", "streamlit_demo.py"]
11-
#EXPOSE 8501
12-
139
# Flask
14-
CMD ["python", "flask_demo.py"]
10+
CMD ["sh", "launch_app.sh"]
1511
EXPOSE 5601
File renamed without changes.

flask/flask_demo.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import os
2+
from multiprocessing.managers import BaseManager
3+
from flask import Flask, request
4+
from werkzeug.utils import secure_filename
5+
6+
app = Flask(__name__)
7+
8+
# initialize manager connection
9+
# NOTE: you might want to handle the password in a less hardcoded way
10+
manager = BaseManager(('', 5602), b'password')
11+
manager.register('query_index')
12+
manager.register('insert_into_index')
13+
manager.connect()
14+
15+
16+
@app.route("/query", methods=["GET"])
17+
def query_index():
18+
global manager
19+
query_text = request.args.get("text", None)
20+
if query_text is None:
21+
return "No text found, please include a ?text=blah parameter in the URL", 400
22+
23+
response = manager.query_index(query_text)
24+
return str(response), 200
25+
26+
27+
@app.route("/upload", methods=["POST"])
28+
def upload_file():
29+
if 'file' not in request.files:
30+
return "Please send a POST request with a file", 400
31+
32+
uploaded_file = request.files["file"]
33+
filename = secure_filename(uploaded_file.filename)
34+
if request.form.get("filename_as_doc_id", None) is not None:
35+
manager.insert_into_index(uploaded_file.read().decode(), doc_id=filename)
36+
else:
37+
manager.insert_into_index(uploaded_file.read().decode())
38+
39+
return "File inserted!", 200
40+
41+
42+
@app.route("/")
43+
def home():
44+
return "Hello, World! Welcome to the llama_index docker image!"
45+
46+
47+
if __name__ == "__main__":
48+
app.run(host="0.0.0.0", port=5601)

flask/index.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

flask/index_server.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import os
2+
3+
# NOTE: for local testing only, do NOT deploy with your key hardcoded
4+
os.environ['OPENAI_API_KEY'] = "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"
5+
6+
from multiprocessing import Lock
7+
from multiprocessing.managers import BaseManager
8+
from llama_index import SimpleDirectoryReader, GPTSimpleVectorIndex, Document
9+
10+
index = None
11+
lock = Lock()
12+
13+
index_name = "./index.json"
14+
documents_folder = "./documents"
15+
16+
17+
def initialize_index():
18+
"""Create a new global index, or load one from the pre-set path."""
19+
global index
20+
21+
with lock:
22+
if os.path.exists(index_name):
23+
index = GPTSimpleVectorIndex.load_from_disk(index_name)
24+
else:
25+
documents = SimpleDirectoryReader(documents_folder).load_data()
26+
index = GPTSimpleVectorIndex(documents)
27+
index.save_to_disk(index_name)
28+
29+
30+
def query_index(query_text):
31+
"""Query the global index."""
32+
global index
33+
response = index.query(query_text)
34+
return str(response).strip()
35+
36+
37+
def insert_into_index(doc_text, doc_id=None):
38+
"""Insert new document into global index."""
39+
global index
40+
print(doc_text)
41+
document = Document(doc_text)
42+
if doc_id is not None:
43+
document.doc_id = doc_id
44+
45+
with lock:
46+
index.insert(document)
47+
48+
49+
if __name__ == "__main__":
50+
# init the global index
51+
print("initializing index...")
52+
initialize_index()
53+
54+
# setup server
55+
# NOTE: you might want to handle the password in a less hardcoded way
56+
manager = BaseManager(('', 5602), b'password')
57+
manager.register('query_index', query_index)
58+
manager.register('insert_into_index', insert_into_index)
59+
server = manager.get_server()
60+
61+
print("starting server...")
62+
server.serve_forever()

flask/launch_app.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
# start backend index server
4+
python ./index_server.py &
5+
6+
# wait for the server to start - if creating a brand new huge index, on startup, increase this further
7+
sleep 60
8+
9+
# start the flask server
10+
python ./flask_demo.py
43.8 KB
Loading
36.1 KB
Loading

0 commit comments

Comments
 (0)