Spaces:
Sleeping
Sleeping
Janar Ramalingam
commited on
Commit
•
51a7f02
1
Parent(s):
2547c6e
first cut working version
Browse files- .gitignore +36 -0
- Dockerfile +22 -0
- README.md +0 -0
- api/db/vector_store.py +11 -0
- api/exception.py +9 -0
- api/main.py +36 -0
- api/routes/admin.py +13 -0
- api/routes/embeddings.py +15 -0
- api/routes/search.py +50 -0
- requirements.txt +11 -0
- setup.py +16 -0
.gitignore
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Byte-compiled / optimized / DLL files
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
|
6 |
+
# Logs and databases
|
7 |
+
*.log
|
8 |
+
*.sqlite3
|
9 |
+
*.db
|
10 |
+
|
11 |
+
# Testing directories
|
12 |
+
/.pytest_cache/
|
13 |
+
/nose-timer-master/
|
14 |
+
|
15 |
+
# Environment and compiled files
|
16 |
+
.env
|
17 |
+
.env.local
|
18 |
+
.env.*.local
|
19 |
+
*.pyc
|
20 |
+
*.swp
|
21 |
+
*.bak
|
22 |
+
|
23 |
+
# IDE files
|
24 |
+
.vscode/
|
25 |
+
.idea/
|
26 |
+
*.iml
|
27 |
+
|
28 |
+
# Distribution / packaging
|
29 |
+
/dist/
|
30 |
+
/build/
|
31 |
+
*.egg-info/
|
32 |
+
|
33 |
+
# Project specific
|
34 |
+
/config.yml
|
35 |
+
/instance/
|
36 |
+
/static/
|
Dockerfile
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use an official Python base image
|
2 |
+
FROM python:3.10-slim
|
3 |
+
|
4 |
+
# Set the working directory
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Copy the requirements.txt file into the container
|
8 |
+
COPY requirements.txt .
|
9 |
+
|
10 |
+
# Install any needed packages specified in requirements.txt
|
11 |
+
RUN pip install --trusted-host pypi.python.org -r requirements.txt
|
12 |
+
|
13 |
+
# Copy the rest of the application code into the container
|
14 |
+
COPY api .
|
15 |
+
|
16 |
+
# Expose the port the app runs on
|
17 |
+
EXPOSE 8080
|
18 |
+
|
19 |
+
ENV OPENAI_API_KEY=zzz
|
20 |
+
ENV QDRANT_API_KEY=yyy
|
21 |
+
# Start the application using Uvicorn
|
22 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
|
README.md
ADDED
File without changes
|
api/db/vector_store.py
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from qdrant_client import QdrantClient
|
3 |
+
from langchain.embeddings import OpenAIEmbeddings
|
4 |
+
from langchain.vectorstores import Qdrant
|
5 |
+
|
6 |
+
embeddings = OpenAIEmbeddings()
|
7 |
+
client = QdrantClient(url="https://32f125d3-5ab1-4058-a10a-bd38a1ebd647.us-east-1-0.aws.cloud.qdrant.io",
|
8 |
+
api_key=os.getenv("QDRANT_API_KEY"))
|
9 |
+
|
10 |
+
def get_instance(collection: str = "test"):
|
11 |
+
return Qdrant(client=client,collection_name=collection,embeddings=embeddings)
|
api/exception.py
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI, UploadFile, File, HTTPException, Request
|
2 |
+
from fastapi.responses import JSONResponse
|
3 |
+
from typing import Any, Dict
|
4 |
+
|
5 |
+
async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
6 |
+
return JSONResponse(
|
7 |
+
status_code=500,
|
8 |
+
content={"message": "Internal server error"},
|
9 |
+
)
|
api/main.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
from fastapi import FastAPI
|
3 |
+
from routes import embeddings, search, admin
|
4 |
+
from fastapi.middleware import Middleware
|
5 |
+
from fastapi.middleware.cors import CORSMiddleware
|
6 |
+
from datetime import datetime
|
7 |
+
from uvicorn.logging import DefaultFormatter
|
8 |
+
import logging
|
9 |
+
import uvicorn
|
10 |
+
from exception import generic_exception_handler
|
11 |
+
|
12 |
+
logger = logging.getLogger(__name__)
|
13 |
+
handler = logging.StreamHandler()
|
14 |
+
handler.setFormatter(DefaultFormatter())
|
15 |
+
logger.addHandler(handler)
|
16 |
+
|
17 |
+
# Create the FastAPI instance
|
18 |
+
app = FastAPI()
|
19 |
+
app.include_router(embeddings.router)
|
20 |
+
app.include_router(search.router)
|
21 |
+
app.include_router(admin.router)
|
22 |
+
app.exception_handler(generic_exception_handler)
|
23 |
+
|
24 |
+
@app.middleware("http")
|
25 |
+
async def log_requests(request, call_next):
|
26 |
+
start_time = datetime.utcnow()
|
27 |
+
response = await call_next(request)
|
28 |
+
process_time = (datetime.utcnow() - start_time).total_seconds() * 1000
|
29 |
+
print(
|
30 |
+
f"{request.method} {request.url.path} {response.status_code} {process_time:.2f}ms"
|
31 |
+
)
|
32 |
+
return response
|
33 |
+
|
34 |
+
|
35 |
+
if __name__ == "__main__":
|
36 |
+
uvicorn.run(app, host="0.0.0.0", port=8080)
|
api/routes/admin.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#This is to init the vector store
|
2 |
+
|
3 |
+
from qdrant_client.models import VectorParams, Distance
|
4 |
+
from fastapi import APIRouter
|
5 |
+
from db import vector_store
|
6 |
+
|
7 |
+
router = APIRouter()
|
8 |
+
|
9 |
+
@router.put("/admin/v1/db")
|
10 |
+
async def recreate_collection(name: str = "test"):
|
11 |
+
print(f"creating collection {name} in db")
|
12 |
+
return vector_store.client.recreate_collection(collection_name=name,
|
13 |
+
vectors_config=VectorParams(size=1536, distance=Distance.COSINE))
|
api/routes/embeddings.py
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, UploadFile, File
|
2 |
+
import openai
|
3 |
+
import io
|
4 |
+
import os
|
5 |
+
from pypdf import PdfReader
|
6 |
+
|
7 |
+
router = APIRouter()
|
8 |
+
|
9 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
10 |
+
|
11 |
+
@router.post("/v1/embeddings")
|
12 |
+
async def embed_doc(file: UploadFile = File(...)):
|
13 |
+
#for now just truncate based on length of words
|
14 |
+
content = await file.read()
|
15 |
+
return openai.Embedding.create(input = content.decode("utf-8"), model = "text-embedding-ada-002")
|
api/routes/search.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, UploadFile, File
|
2 |
+
from fastapi.responses import JSONResponse
|
3 |
+
import openai
|
4 |
+
import io
|
5 |
+
import os
|
6 |
+
from pypdf import PdfReader
|
7 |
+
from langchain.embeddings.openai import OpenAIEmbeddings
|
8 |
+
from langchain.vectorstores import Qdrant
|
9 |
+
from langchain.schema import Document
|
10 |
+
from langchain.chains.question_answering import load_qa_chain
|
11 |
+
from langchain.llms import OpenAI
|
12 |
+
from db import vector_store
|
13 |
+
|
14 |
+
router = APIRouter()
|
15 |
+
_db = vector_store.get_instance()
|
16 |
+
_chain = load_qa_chain(OpenAI(temperature=0), chain_type="stuff")
|
17 |
+
|
18 |
+
@router.post("/v1/docs")
|
19 |
+
async def index_doc(file: UploadFile = File(...)):
|
20 |
+
async for doc in generate_documents(file):
|
21 |
+
_db.add_documents([doc])
|
22 |
+
#todo return something sensible
|
23 |
+
return JSONResponse(status_code=200, content={})
|
24 |
+
|
25 |
+
@router.get("/v1/docs")
|
26 |
+
async def search(query: str):
|
27 |
+
print(query)
|
28 |
+
docs = _db.similarity_search(query=query)
|
29 |
+
answer = _chain.run(input_documents=docs, question=query)
|
30 |
+
return JSONResponse(status_code=200, content={"answer": answer})
|
31 |
+
|
32 |
+
async def generate_documents(file: UploadFile):
|
33 |
+
num=0
|
34 |
+
async for txt in convert_documents(file):
|
35 |
+
num += 1
|
36 |
+
document = Document(page_content=txt,metadata={"page": num})
|
37 |
+
yield document
|
38 |
+
|
39 |
+
async def convert_documents(file: UploadFile):
|
40 |
+
#parse pdf document
|
41 |
+
if file.content_type == 'application/pdf':
|
42 |
+
content = await file.read()
|
43 |
+
pdf_reader = PdfReader(io.BytesIO(content))
|
44 |
+
try:
|
45 |
+
for page in pdf_reader.pages:
|
46 |
+
yield page.extract_text()
|
47 |
+
except Exception as e:
|
48 |
+
print(f"Exception {e}")
|
49 |
+
else:
|
50 |
+
return
|
requirements.txt
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
openai
|
2 |
+
uvicorn
|
3 |
+
fastapi
|
4 |
+
python-multipart
|
5 |
+
numpy
|
6 |
+
pypdf
|
7 |
+
langchain
|
8 |
+
tiktoken
|
9 |
+
faiss-cpu
|
10 |
+
qdrant-client
|
11 |
+
|
setup.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from setuptools import setup, find_packages
|
2 |
+
|
3 |
+
with open("requirements.txt") as f:
|
4 |
+
requirements = f.read().splitlines()
|
5 |
+
|
6 |
+
setup(
|
7 |
+
name="doc_index",
|
8 |
+
version="0.1",
|
9 |
+
packages=find_packages(),
|
10 |
+
install_requires= requirements,
|
11 |
+
entry_points={
|
12 |
+
"console_scripts": [
|
13 |
+
# add any command-line scripts here
|
14 |
+
]
|
15 |
+
},
|
16 |
+
)
|