med-bot / main.py
chintu4's picture
update
4d7f4b5
Raw
History Blame Contribute Delete
6.1 kB
import argparse
from pathlib import Path
import torch
from sentence_transformers import SentenceTransformer, util
DEFAULT_PICKLE = Path(__file__).parent / "rag_store_cpu.pkl"
DEFAULT_MODEL = "BAAI/bge-large-en-v1.5"
GOOGLE_DRIVE_FILE_ID = "1RLtLARA0G0v51CQckpQqfXeD10RSSA39"
GOOGLE_DRIVE_DOWNLOAD_URL = "https://drive.google.com/uc?export=download"
def get_device():
if torch.cuda.is_available():
return "cuda"
if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
return "mps"
return "cpu"
def _get_google_drive_confirm_token(text):
import re
match = re.search(r"confirm=([0-9A-Za-z_\-]+)&", text)
if match:
return match.group(1)
match = re.search(r"confirm=([0-9A-Za-z_\-]+)\"", text)
if match:
return match.group(1)
return None
def download_google_drive_file(dest_path, file_id=GOOGLE_DRIVE_FILE_ID):
import requests
dest_path = Path(dest_path)
dest_path.parent.mkdir(parents=True, exist_ok=True)
session = requests.Session()
response = session.get(
GOOGLE_DRIVE_DOWNLOAD_URL,
params={"id": file_id},
stream=True,
timeout=30,
)
content_type = response.headers.get("Content-Type", "")
if "Content-Disposition" not in response.headers and "text/html" in content_type:
token = _get_google_drive_confirm_token(response.text)
if token:
response = session.get(
GOOGLE_DRIVE_DOWNLOAD_URL,
params={"id": file_id, "confirm": token},
stream=True,
timeout=30,
)
if response.status_code != 200:
raise RuntimeError(
f"Failed to download file from Google Drive (status {response.status_code})"
)
with open(dest_path, "wb") as f:
for chunk in response.iter_content(chunk_size=32768):
if chunk:
f.write(chunk)
return dest_path
def ensure_store(path):
path = Path(path)
if path.exists():
return path
if path.name == DEFAULT_PICKLE.name:
print(
f"Store not found at {path}. Downloading default vector store from Google Drive..."
)
download_google_drive_file(path)
print(f"Downloaded store to {path}")
return path
raise FileNotFoundError(f"Store not found: {path}")
def load_store(path):
import pickle
store_path = ensure_store(path)
with open(store_path, "rb") as f:
return pickle.load(f)
def ensure_tensor(embeddings):
if torch.is_tensor(embeddings):
return embeddings.float().cpu()
return torch.tensor(
embeddings,
dtype=torch.float32,
)
def load_model(model_name, device):
print(f"Loading model: {model_name}")
print(f"Device: {device}")
return SentenceTransformer(
model_name,
device=device,
)
def encode_query(model, query):
query = (
"Represent this sentence for searching relevant passages: "
+ query
)
return model.encode(
query,
convert_to_tensor=True,
normalize_embeddings=True,
)
def retrieve_top_k(
query,
embeddings,
texts,
model,
top_k=5,
threshold=0.35,
):
query_embedding = encode_query(
model,
query,
)
embeddings = ensure_tensor(
embeddings
)
scores = util.cos_sim(
query_embedding,
embeddings,
)[0]
top_k = min(
top_k,
len(texts),
)
top_results = torch.topk(
scores,
k=top_k,
)
results = []
for idx, score in zip(
top_results.indices,
top_results.values,
):
score = float(score)
if score >= threshold:
results.append(
(
score,
texts[idx.item()],
)
)
return results
def print_environment():
print("\n=== Environment ===")
print("PyTorch:", torch.__version__)
print("CUDA Available:", torch.cuda.is_available())
if torch.cuda.is_available():
print(
"GPU:",
torch.cuda.get_device_name(0),
)
print("===================\n")
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--path",
default=str(DEFAULT_PICKLE),
)
parser.add_argument(
"--query",
default="what is diabetes",
)
parser.add_argument(
"--top-k",
type=int,
default=5,
)
parser.add_argument(
"--threshold",
type=float,
default=0.35,
)
parser.add_argument(
"--model",
default=DEFAULT_MODEL,
)
args = parser.parse_args()
print_environment()
store_path = Path(args.path)
if not store_path.exists():
raise FileNotFoundError(
f"Store not found: {store_path}"
)
print("Loading vector store...")
store = load_store(store_path)
embeddings = ensure_tensor(
store["embeddings"]
)
texts = store["texts"]
device = get_device()
model = load_model(
args.model,
device,
)
results = retrieve_top_k(
query=args.query,
embeddings=embeddings,
texts=texts,
model=model,
top_k=args.top_k,
threshold=args.threshold,
)
print("\n=== Results ===\n")
if not results:
print(
"No documents found above threshold."
)
return
context = []
for i, (score, text) in enumerate(
results,
start=1,
):
print(
f"Result {i} | Score: {score:.4f}\n"
)
print(text[:1000])
print(
"\n"
+ "=" * 80
+ "\n"
)
context.append(
f"[Document {i}]\n{text}"
)
final_context = "\n\n".join(
context
)
print(
"\nContext length:",
len(final_context),
)
if __name__ == "__main__":
main()