pdfparsing / main.py
uppalmaurya's picture
Fix uvicorn runtime error by using import string
7434732
Raw
History Blame Contribute Delete
9.84 kB
from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import os
import uuid
from pathlib import Path
from typing import Dict, List, Optional
import logging
from datetime import datetime
import json
from pdf_processor import PDFProcessor
from mcp_server import update_document_data, remove_document
from anthropic_client import AnthropicClient
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(title="PDF Parser MCP Server", version="1.0.0")
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Initialize components
pdf_processor = PDFProcessor()
anthropic_client = AnthropicClient()
# Storage for processed documents
processed_documents: Dict[str, Dict] = {}
# Ensure upload directory exists
UPLOAD_DIR = Path(os.getenv("UPLOAD_DIR", "uploads"))
UPLOAD_DIR.mkdir(exist_ok=True)
STORAGE_FILE = Path("storage.json")
def save_documents():
with open(STORAGE_FILE, "w", encoding="utf-8") as f:
json.dump(processed_documents, f, ensure_ascii=False, indent=2)
def load_documents():
global processed_documents
if STORAGE_FILE.exists():
with open(STORAGE_FILE, "r", encoding="utf-8") as f:
processed_documents.clear()
processed_documents.update(json.load(f))
# Load documents on startup
load_documents()
@app.get("/")
async def root():
"""Welcome message and API information"""
return {
"message": "PDF Parser MCP Server API",
"version": "1.0.0",
"docs": "/docs",
"health": "/health",
"endpoints": {
"upload": "POST /upload-pdf/",
"list_documents": "GET /documents/",
"document_info": "GET /documents/{file_id}",
"document_content": "GET /documents/{file_id}/content",
"document_summary": "GET /documents/{file_id}/summary",
"document_chunks": "GET /documents/{file_id}/chunks",
"full_document": "GET /documents/{file_id}/full"
}
}
@app.post("/upload-pdf/")
async def upload_pdf(
background_tasks: BackgroundTasks,
file: UploadFile = File(...)
):
"""Upload and process a PDF file"""
# Validate file type
if not file.filename.endswith('.pdf'):
raise HTTPException(status_code=400, detail="Only PDF files are allowed")
# Generate unique file ID
file_id = str(uuid.uuid4())
file_path = UPLOAD_DIR / f"{file_id}.pdf"
try:
# Save uploaded file
with open(file_path, "wb") as buffer:
content = await file.read()
buffer.write(content)
# Process PDF in background
background_tasks.add_task(process_pdf_background, file_id, file_path, file.filename)
return JSONResponse({
"file_id": file_id,
"filename": file.filename,
"status": "processing",
"message": "PDF uploaded successfully. Processing in background."
})
except Exception as e:
logger.error(f"Error uploading file: {str(e)}")
raise HTTPException(status_code=500, detail=f"Error uploading file: {str(e)}")
async def process_pdf_background(file_id: str, file_path: Path, filename: str):
"""Background task to process PDF"""
try:
logger.info(f"Processing PDF: {filename}")
# Extract text from PDF
extracted_text = pdf_processor.extract_text(file_path)
# Chunk text if needed
chunks = pdf_processor.chunk_text(extracted_text)
# Generate summary using Anthropic
summary = await anthropic_client.generate_summary(chunks)
# Store processed document
processed_documents[file_id] = {
"filename": filename,
"extracted_text": extracted_text,
"chunks": chunks,
"summary": summary,
"processed_at": datetime.now().isoformat(),
"status": "completed"
}
# Update MCP server data
update_document_data(file_id, processed_documents[file_id])
logger.info(f"Successfully processed PDF: {filename}")
except Exception as e:
logger.error(f"Error processing PDF {filename}: {str(e)}")
processed_documents[file_id] = {
"filename": filename,
"status": "error",
"error": str(e),
"processed_at": datetime.now().isoformat()
}
finally:
save_documents()
@app.get("/status/{file_id}")
async def get_processing_status(file_id: str):
"""Get processing status of a PDF"""
if file_id not in processed_documents:
raise HTTPException(status_code=404, detail="File not found")
doc = processed_documents[file_id]
return {
"file_id": file_id,
"filename": doc.get("filename"),
"status": doc.get("status"),
"processed_at": doc.get("processed_at"),
"has_summary": "summary" in doc,
"error": doc.get("error")
}
@app.get("/documents/")
async def list_documents():
"""List all processed documents"""
return {
"documents": [
{
"file_id": file_id,
"filename": doc.get("filename"),
"status": doc.get("status"),
"processed_at": doc.get("processed_at"),
"has_text": "extracted_text" in doc,
"has_summary": "summary" in doc,
"has_chunks": "chunks" in doc
}
for file_id, doc in processed_documents.items()
]
}
@app.get("/documents/{file_id}")
async def get_document_info(file_id: str):
"""Get document metadata and info"""
if file_id not in processed_documents:
raise HTTPException(status_code=404, detail="Document not found")
doc = processed_documents[file_id]
return {
"file_id": file_id,
"filename": doc.get("filename"),
"status": doc.get("status"),
"processed_at": doc.get("processed_at"),
"has_text": "extracted_text" in doc,
"has_summary": "summary" in doc,
"has_chunks": "chunks" in doc,
"text_length": len(doc.get("extracted_text", "")),
"num_chunks": len(doc.get("chunks", [])),
"error": doc.get("error")
}
@app.get("/documents/{file_id}/content")
async def get_document_content(file_id: str):
"""Get extracted text content of a document"""
if file_id not in processed_documents:
raise HTTPException(status_code=404, detail="Document not found")
doc = processed_documents[file_id]
if "extracted_text" not in doc:
raise HTTPException(status_code=404, detail="Extracted text not available")
return {
"file_id": file_id,
"filename": doc.get("filename"),
"extracted_text": doc["extracted_text"],
"text_length": len(doc["extracted_text"])
}
@app.get("/documents/{file_id}/summary")
async def get_document_summary(file_id: str):
"""Get AI-generated summary of a document"""
if file_id not in processed_documents:
raise HTTPException(status_code=404, detail="Document not found")
doc = processed_documents[file_id]
if "summary" not in doc:
raise HTTPException(status_code=404, detail="Summary not available")
return {
"file_id": file_id,
"filename": doc.get("filename"),
"summary": doc["summary"]
}
@app.get("/documents/{file_id}/chunks")
async def get_document_chunks(file_id: str):
"""Get text chunks of a document"""
if file_id not in processed_documents:
raise HTTPException(status_code=404, detail="Document not found")
doc = processed_documents[file_id]
if "chunks" not in doc:
raise HTTPException(status_code=404, detail="Chunks not available")
return {
"file_id": file_id,
"filename": doc.get("filename"),
"chunks": doc["chunks"],
"num_chunks": len(doc["chunks"])
}
@app.get("/documents/{file_id}/full")
async def get_full_document(file_id: str):
"""Get complete document data (text, summary, chunks)"""
if file_id not in processed_documents:
raise HTTPException(status_code=404, detail="Document not found")
return processed_documents[file_id]
@app.delete("/documents/{file_id}")
async def delete_document(file_id: str):
"""Delete a processed document"""
if file_id not in processed_documents:
raise HTTPException(status_code=404, detail="Document not found")
# Remove from storage
del processed_documents[file_id]
# Remove from MCP server
remove_document(file_id)
# Remove uploaded file
file_path = UPLOAD_DIR / f"{file_id}.pdf"
if file_path.exists():
file_path.unlink()
save_documents()
return {"message": "Document deleted successfully"}
# Keep the old endpoints for backwards compatibility
@app.get("/document/{file_id}")
async def get_document(file_id: str):
"""Get full document data (deprecated - use /documents/{file_id}/full)"""
return await get_full_document(file_id)
@app.delete("/document/{file_id}")
async def delete_document_old(file_id: str):
"""Delete a processed document (deprecated - use /documents/{file_id})"""
return await delete_document(file_id)
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {"status": "healthy", "timestamp": datetime.now().isoformat()}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)