Spaces:
Runtime error
Runtime error
Upload 15 files
Browse files- .dockerignore +13 -0
- .gitattributes +2 -0
- .gitignore +4 -0
- Dockerfile +0 -0
- app.py +41 -0
- chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/data_level0.bin +3 -0
- chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/header.bin +3 -0
- chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/index_metadata.pickle +3 -0
- chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/length.bin +3 -0
- chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/link_lists.bin +3 -0
- chroma_db/chroma.sqlite3 +3 -0
- data/MedicalBook.pdf +3 -0
- engine.py +135 -0
- main.py +34 -0
- requirements.txt +18 -0
- start.sh +0 -0
.dockerignore
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
venv/
|
| 2 |
+
__pycache__/
|
| 3 |
+
.env
|
| 4 |
+
.ipynb_checkpoints/
|
| 5 |
+
*.pyc
|
| 6 |
+
|
| 7 |
+
# Exclude any local model weights if you accidentally put them in the folder
|
| 8 |
+
*.bin
|
| 9 |
+
*.bin.*
|
| 10 |
+
*.pth
|
| 11 |
+
*.pt
|
| 12 |
+
model/
|
| 13 |
+
checkpoint/
|
.gitattributes
CHANGED
|
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
chroma_db/chroma.sqlite3 filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
data/MedicalBook.pdf filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
venv/
|
| 2 |
+
.env
|
| 3 |
+
chroma_db/
|
| 4 |
+
__pycache__/
|
Dockerfile
ADDED
|
File without changes
|
app.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import requests # Standard library for sending API requests
|
| 3 |
+
|
| 4 |
+
st.set_page_config(page_title="Medical AI Assistant", layout="centered")
|
| 5 |
+
st.title("⚕️ General Medicine Chatbot")
|
| 6 |
+
|
| 7 |
+
# This is the address of your FastAPI "Brain"
|
| 8 |
+
API_URL = "http://127.0.0.1:8000/ask"
|
| 9 |
+
|
| 10 |
+
# Initialize chat history
|
| 11 |
+
if "messages" not in st.session_state:
|
| 12 |
+
st.session_state.messages = []
|
| 13 |
+
|
| 14 |
+
# Display previous messages
|
| 15 |
+
for message in st.session_state.messages:
|
| 16 |
+
with st.chat_message(message["role"]):
|
| 17 |
+
st.markdown(message["content"])
|
| 18 |
+
|
| 19 |
+
if prompt := st.chat_input("Ask a medical question..."):
|
| 20 |
+
# 1. Display user message
|
| 21 |
+
st.chat_message("user").markdown(prompt)
|
| 22 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 23 |
+
|
| 24 |
+
# 2. Call the API
|
| 25 |
+
with st.chat_message("assistant"):
|
| 26 |
+
with st.spinner("Thinking..."):
|
| 27 |
+
try:
|
| 28 |
+
# We send the query to your FastAPI server
|
| 29 |
+
# Note: 'query' must match the Pydantic model in main.py
|
| 30 |
+
payload = {"query": prompt}
|
| 31 |
+
response = requests.post(API_URL, json=payload)
|
| 32 |
+
|
| 33 |
+
if response.status_code == 200:
|
| 34 |
+
answer = response.json().get("answer", "No answer found.")
|
| 35 |
+
st.markdown(answer)
|
| 36 |
+
st.session_state.messages.append({"role": "assistant", "content": answer})
|
| 37 |
+
else:
|
| 38 |
+
st.error(f"API Error {response.status_code}: {response.text}")
|
| 39 |
+
|
| 40 |
+
except Exception as e:
|
| 41 |
+
st.error(f"Could not connect to the API. Is Uvicorn running? Error: {e}")
|
chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/data_level0.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a7d011826d12a6ecb524c46ede71a830b5c48fd6f1cce9fccc4769ab833c3f7a
|
| 3 |
+
size 30484764
|
chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/header.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4cc1e2b6bdf1eee6eeddf6cb9aa7e20c69033a8a929d902cd56859392e703544
|
| 3 |
+
size 100
|
chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/index_metadata.pickle
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9cddbf2ed05e967b82f0aa08af3fc6d86d674784c779fc12911e6b4c0513a534
|
| 3 |
+
size 1673588
|
chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/length.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8e94d1a8852d94aa7c25b56188ab929e437a50bcce95ffd81eacfc29596156ec
|
| 3 |
+
size 72756
|
chroma_db/3680a8e6-9770-4cfc-9ae4-18ad2a369d9b/link_lists.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4a0204e0094ab2daaecd241d7808b915e3c05cf6f38abed8cba205a33303304e
|
| 3 |
+
size 156260
|
chroma_db/chroma.sqlite3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:004ed53cdb169fca6e2874342756070a0d218cf9d3856ddd4395176bb58f83c9
|
| 3 |
+
size 118001664
|
data/MedicalBook.pdf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cef47d2bcde526111d3d29f77876ab139efdf7d7e5e3180838fff9c335c64dc1
|
| 3 |
+
size 10764110
|
engine.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from operator import itemgetter
|
| 2 |
+
import os
|
| 3 |
+
from dotenv import load_dotenv
|
| 4 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
| 5 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
| 6 |
+
from langchain_chroma import Chroma
|
| 7 |
+
|
| 8 |
+
from utils import load_docs, split_docs
|
| 9 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
| 10 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 11 |
+
from langchain_core.output_parsers import StrOutputParser
|
| 12 |
+
from langchain_core.runnables import RunnablePassthrough
|
| 13 |
+
from langchain_huggingface import ChatHuggingFace
|
| 14 |
+
|
| 15 |
+
import torch
|
| 16 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
|
| 17 |
+
from langchain_huggingface import HuggingFacePipeline
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
import warnings
|
| 21 |
+
warnings.filterwarnings("ignore", category=UserWarning)
|
| 22 |
+
|
| 23 |
+
load_dotenv()
|
| 24 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 25 |
+
file_path = "data/MedicalBook.pdf"
|
| 26 |
+
|
| 27 |
+
def downlode_hugging_face_embeddings():
|
| 28 |
+
embeddings=HuggingFaceEmbeddings(model_name='sentence-transformers/msmarco-MiniLM-L6-v3')
|
| 29 |
+
return embeddings
|
| 30 |
+
|
| 31 |
+
CHROMA_PATH="chroma_db"
|
| 32 |
+
#embeddings = downlode_hugging_face_embeddings()
|
| 33 |
+
|
| 34 |
+
def create_vectorstore(embeddings):
|
| 35 |
+
if os.path.exists(CHROMA_PATH):
|
| 36 |
+
print(f"--- Loading existing Vector DB from {CHROMA_PATH}... ---")
|
| 37 |
+
# FIX: To LOAD, use Chroma() directly. Do NOT use .from_documents()
|
| 38 |
+
return Chroma(
|
| 39 |
+
persist_directory=CHROMA_PATH,
|
| 40 |
+
embedding_function=embeddings
|
| 41 |
+
)
|
| 42 |
+
else:
|
| 43 |
+
print("Creating a new one...")
|
| 44 |
+
documents = load_docs(file_path)
|
| 45 |
+
text_chunks = split_docs(documents)
|
| 46 |
+
# Use .from_documents ONLY when you have new text_chunks to process
|
| 47 |
+
return Chroma.from_documents(
|
| 48 |
+
documents=text_chunks,
|
| 49 |
+
embedding=embeddings,
|
| 50 |
+
persist_directory=CHROMA_PATH
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
# vector_db=create_vectorstore()
|
| 54 |
+
# retriever=vector_db.as_retriever(search_kwargs={"k": 3})
|
| 55 |
+
|
| 56 |
+
def get_llm():
|
| 57 |
+
# The correct ID for the 1B Instruct model
|
| 58 |
+
model_path = "ibm-granite/granite-3.0-1b-a400m-instruct"
|
| 59 |
+
|
| 60 |
+
device = -1 # Force CPU
|
| 61 |
+
print(f"--- Loading local model: {model_path} ---")
|
| 62 |
+
|
| 63 |
+
tokenizer = AutoTokenizer.from_pretrained(model_path)
|
| 64 |
+
|
| 65 |
+
# We use low_cpu_mem_usage to keep the RAM footprint small
|
| 66 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 67 |
+
model_path,
|
| 68 |
+
torch_dtype=torch.float32,
|
| 69 |
+
low_cpu_mem_usage=True,
|
| 70 |
+
device_map=None
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
gen_pipeline = pipeline(
|
| 74 |
+
"text-generation",
|
| 75 |
+
model=model,
|
| 76 |
+
tokenizer=tokenizer,
|
| 77 |
+
max_new_tokens=256,
|
| 78 |
+
temperature=0.3,
|
| 79 |
+
do_sample=True,
|
| 80 |
+
return_full_text=False
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
llm = HuggingFacePipeline(pipeline=gen_pipeline)
|
| 84 |
+
return ChatHuggingFace(llm=llm)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def get_llm_response(user_input, retriever, llm):
|
| 88 |
+
prompt = ChatPromptTemplate.from_messages([
|
| 89 |
+
("system", "You are a medical specialist. Use the context to answer. If you don't know, say 'I don't know'."),
|
| 90 |
+
("human", "Context: {context}\n\nQuestion: {input}")
|
| 91 |
+
])
|
| 92 |
+
|
| 93 |
+
chain = (
|
| 94 |
+
{
|
| 95 |
+
"context": itemgetter("input") | retriever,
|
| 96 |
+
"input": itemgetter("input"),
|
| 97 |
+
}
|
| 98 |
+
| prompt
|
| 99 |
+
| llm
|
| 100 |
+
| StrOutputParser()
|
| 101 |
+
)
|
| 102 |
+
return chain.invoke({"input": user_input})
|
| 103 |
+
|
| 104 |
+
def load_system():
|
| 105 |
+
"""
|
| 106 |
+
Orchestrates the loading of the full RAG pipeline.
|
| 107 |
+
This can be called by both FastAPI and Streamlit.
|
| 108 |
+
"""
|
| 109 |
+
print("--- Initializing Medical AI System ---")
|
| 110 |
+
embeddings = downlode_hugging_face_embeddings()
|
| 111 |
+
vector_db = create_vectorstore(embeddings)
|
| 112 |
+
retriever = vector_db.as_retriever(search_kwargs={"k": 3})
|
| 113 |
+
llm = get_llm()
|
| 114 |
+
print("--- System Ready ---")
|
| 115 |
+
return retriever, llm
|
| 116 |
+
|
| 117 |
+
if __name__=="__main__":
|
| 118 |
+
|
| 119 |
+
embeddings = downlode_hugging_face_embeddings()
|
| 120 |
+
vector_db=create_vectorstore(embeddings)
|
| 121 |
+
retriever=vector_db.as_retriever(search_kwargs={"k": 3})
|
| 122 |
+
llm=get_llm()
|
| 123 |
+
|
| 124 |
+
# print("\n -- LLM Testing -- \n")
|
| 125 |
+
# while True:
|
| 126 |
+
# User_query=input("User: ")
|
| 127 |
+
# if User_query.lower() in ["quit","exit","q"]:
|
| 128 |
+
# print("Exiting..")
|
| 129 |
+
# break
|
| 130 |
+
# response=get_llm_response(User_query, retriever, llm)
|
| 131 |
+
# print(f"Assistant: {response}")
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
|
main.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException
|
| 2 |
+
from pydantic import BaseModel
|
| 3 |
+
from engine import load_system, get_llm_response
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
app=FastAPI(
|
| 7 |
+
title="Medical RAG API",
|
| 8 |
+
description="Backend API for IBM Granite chat model",
|
| 9 |
+
version="1.0.0")
|
| 10 |
+
|
| 11 |
+
#gloabal model loading
|
| 12 |
+
print("Loading Medical AI Engine... Please wait.")
|
| 13 |
+
retriever, llm = load_system()
|
| 14 |
+
print("AI loaded successfully.")
|
| 15 |
+
|
| 16 |
+
class ChatRequest(BaseModel):
|
| 17 |
+
query: str
|
| 18 |
+
|
| 19 |
+
class ChatResponse(BaseModel):
|
| 20 |
+
answer: str
|
| 21 |
+
|
| 22 |
+
@app.get("/")
|
| 23 |
+
def read_root():
|
| 24 |
+
return {"status":"online","message": "Medical API is Ready"}
|
| 25 |
+
|
| 26 |
+
@app.post("/ask", response_model=ChatResponse)
|
| 27 |
+
async def ask_question(request: ChatRequest):
|
| 28 |
+
try:
|
| 29 |
+
# Using the globally loaded retriever and llm
|
| 30 |
+
response = get_llm_response(request.query, retriever, llm)
|
| 31 |
+
return ChatResponse(answer=response)
|
| 32 |
+
except Exception as e:
|
| 33 |
+
# Standard API error handling
|
| 34 |
+
raise HTTPException(status_code=500, detail=str(e))
|
requirements.txt
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn
|
| 3 |
+
Pydentic
|
| 4 |
+
python-dotenv
|
| 5 |
+
|
| 6 |
+
langchain
|
| 7 |
+
langchain-community
|
| 8 |
+
langchain-huggingface
|
| 9 |
+
sentence-transformers
|
| 10 |
+
|
| 11 |
+
pypdf
|
| 12 |
+
langchain-chroma
|
| 13 |
+
streamlit
|
| 14 |
+
|
| 15 |
+
requests
|
| 16 |
+
torch --index-url https://download.pytorch.org/whl/cpu
|
| 17 |
+
transformers
|
| 18 |
+
accelerate
|
start.sh
ADDED
|
File without changes
|