AnatomyLite / pdfimage_server.py
gladguy's picture
Fresh
4c6a071
Raw
History Blame Contribute Delete
5.55 kB
# super_server.py
import os
import fitz # PyMuPDF
from mcp.server.fastmcp import FastMCP
from serpapi import GoogleSearch
from dotenv import load_dotenv
# --- IMPORTS FOR TEXTBOOK (RAG) ---
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.openai import OpenAI as LlamaOpenAI
load_dotenv()
mcp = FastMCP("Anato-Mitra Super Server")
# ==========================================
# 📸 PART 0: IMAGE EXTRACTOR
# ==========================================
IMAGE_DIR = "extracted_images"
def extract_images_from_pdfs():
"""
Runs on startup. Scans PDFs and saves images.
Skips saving if the image already exists.
"""
if not os.path.exists("textbooks"):
os.makedirs("textbooks")
return
if not os.path.exists(IMAGE_DIR):
os.makedirs(IMAGE_DIR)
print("📸 Scanning PDFs for images...")
for pdf_file in os.listdir("textbooks"):
if not pdf_file.endswith(".pdf"):
continue
pdf_path = os.path.join("textbooks", pdf_file)
doc = fitz.open(pdf_path)
for page_index, page in enumerate(doc):
image_list = page.get_images(full=True)
for img_index, img in enumerate(image_list):
xref = img[0]
base_image = doc.extract_image(xref)
image_bytes = base_image["image"]
image_ext = base_image["ext"]
# Naming convention: bookname_page_X_img_Y.png
image_name = f"{pdf_file.replace('.pdf', '')}_page_{page_index + 1}_img_{img_index}.{image_ext}"
image_path = os.path.join(IMAGE_DIR, image_name)
# CHECK: Only save if it doesn't exist
if not os.path.exists(image_path):
with open(image_path, "wb") as f:
f.write(image_bytes)
print(f"✅ PDF Images ready in '{IMAGE_DIR}/'")
extract_images_from_pdfs()
# ==========================================
# 🧠 PART 1: TEXTBOOK KNOWLEDGE (With Page Citations)
# ==========================================
QUERY_ENGINE = None
def initialize_textbook():
global QUERY_ENGINE
try:
print("📚 Configuring Textbook Engine...")
Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
Settings.llm = LlamaOpenAI(
model="meta-llama/Meta-Llama-3.1-70B-Instruct",
api_key=os.getenv("HYPERBOLIC_API_KEY"),
api_base="https://api.hyperbolic.xyz/v1"
)
documents = SimpleDirectoryReader("textbooks").load_data()
if not documents: return
index = VectorStoreIndex.from_documents(documents)
QUERY_ENGINE = index.as_query_engine(similarity_top_k=3)
print("✅ Textbook Indexed!")
except Exception as e:
print(f"❌ Textbook Error: {e}")
initialize_textbook()
@mcp.tool()
def consult_medical_textbook(question: str) -> str:
"""
Consults uploaded medical textbooks.
Returns answer + Diagrams + Page Numbers.
"""
global QUERY_ENGINE
if not QUERY_ENGINE:
return "Textbook unavailable."
try:
# 1. Get Text Answer
response = QUERY_ENGINE.query(question)
text_answer = str(response)
# 2. Find Matching Images & Page Numbers
found_data = [] # We will store (path, page, book) here
for node in response.source_nodes:
page_num = node.metadata.get("page_label")
file_name = node.metadata.get("file_name")
if page_num and file_name:
clean_name = file_name.replace('.pdf', '')
search_prefix = f"{clean_name}_page_{page_num}_"
# Check if we extracted an image from this specific page
for img_file in os.listdir(IMAGE_DIR):
if img_file.startswith(search_prefix):
found_data.append({
"path": os.path.join(IMAGE_DIR, img_file),
"page": page_num,
"book": clean_name
})
# 3. Format the Output
if found_data:
text_answer += "\n\n---\n### 📖 Textbook Reference Diagrams"
# Limit to top 2 images to avoid spamming
for item in found_data[:2]:
# Add Image
text_answer += f"\n![Textbook Diagram]({item['path']})"
# Add Caption with Page Number
text_answer += f"\n\n*Source: **{item['book']}**, Page **{item['page']}***\n"
return text_answer
except Exception as e:
return f"Error: {str(e)}"
# ==========================================
# 👁️ PART 2: EXTERNAL SEARCH
# ==========================================
@mcp.tool()
def search_anatomy_diagrams(topic: str) -> str:
"""Searches Google Images (Backup)."""
api_key = os.getenv("SERPAPI_KEY")
if api_key:
try:
search = GoogleSearch({"q": f"{topic} anatomy schematic", "engine": "google_images", "api_key": api_key})
res = search.get_dict().get("images_results", [])
if res: return f"![Diagram]({res[0]['original']})"
except: pass
return "No external diagram found."
if __name__ == "__main__":
mcp.run()