Textract empowers you to unlock the hidden value within your PDFs. Extract key information, summarize content, and find answers instantly. π
This document provides an overview of the PDF Chatbot application, its architecture, workflow, and API endpoints. The project allows users to upload a PDF file, extract its content, and query the extracted text using a chatbot powered by the Llama-3.3-70B-Versatile model.
- Built using FastAPI.
- Handles file uploads, PDF text extraction, and querying the Llama model for answers based on the uploaded content.
- Utilizes Groq AI's interface with the Llama-3.3-70B-Versatile model for answering user queries.
- Developed using React.js.
- Provides an intuitive user interface for uploading files and interacting with the chatbot.
The /upload/ endpoint handles PDF uploads and extracts text using the PyMuPDF library.
@app.post("/upload/")
async def upload_pdf(file: UploadFile = File(...)):
# Check if the uploaded file is a PDF
if not file.filename.endswith(".pdf"):
raise HTTPException(status_code=400, detail=f"File {file.filename} is not a valid PDF.")
try:
# Read the uploaded file as bytes
pdf_bytes = await file.read()
# Open and process the PDF using PyMuPDF
with fitz.open(stream=pdf_bytes, filetype="pdf") as doc:
extracted_text = ""
# Loop through all the pages in the PDF and extract the text
for page_num in range(doc.page_count):
page = doc[page_num]
extracted_text += page.get_text("text") + "\n" # Extract text in plain format
# Save the extracted text in memory, using the filename as the key
uploaded_pdf_text[file.filename] = extracted_text
# Return a success message with the filename
return {"message": "File uploaded and processed successfully.", "filename": file.filename}
except Exception as e:
# If something goes wrong, send an error response
raise HTTPException(status_code=500, detail=str(e))The /ask/ endpoint handles user queries based on the content of the uploaded PDF. It uses the Llama-3.3-70B-Versatile model for generating answers.
class AskRequest(BaseModel):
filename: str # The name of the uploaded file we want to query
question: str # The question to ask based on the file's content
@app.post("/ask/")
async def ask_question(data: AskRequest):
# Check if the requested file is in our temporary storage
if data.filename not in uploaded_pdf_text:
raise HTTPException(status_code=400, detail="File not found or not yet uploaded.")
try:
# Get the extracted text for the requested file
extracted_text = uploaded_pdf_text[data.filename]
# Use the Llama handler to process the question and get an answer
answer = get_answer_from_llama(extracted_text, data.question)
# Return the question and the generated answer
return {
"question": data.question,
"answer": answer,
}
except Exception as e:
# If something goes wrong, return an error response
raise HTTPException(status_code=500, detail=str(e))The get_answer_from_llama function interacts with the Llama model to generate answers.
def get_answer_from_llama(file_content: str, question: str) -> str:
"""
This function asks the Llama model a question based on some input text and returns its answer.
Arguments:
- file_content: The text or content we want the Llama model to base its answer on.
- question: The actual question weβre asking.
Returns:
- The answer generated by the Llama model as a string.
"""
try:
# Send a message to the Llama model with the content and question.
chat_completion = client.chat.completions.create(
messages=[
{
"role": "user",
"content": f"{file_content} Based on above text {question}",
}
],
model="llama-3.3-70b-versatile",
)
return chat_completion.choices[0].message.content
except Exception as e:
raise RuntimeError(f"Error in Groq client: {e}")The uploadFile function handles file uploads to the backend.
const uploadFile = async (file) => {
const formData = new FormData(); // Create a FormData object for file upload
formData.append('file', file); // Append the file to the FormData object
try {
// Send the file to the backend using Axios
const response = await axios.post('http://localhost:8000/upload/', formData, {
headers: {
'Content-Type': 'multipart/form-data', // Set the appropriate header for file uploads
},
});
console.log('Response:', response.data); // Log the server response for debugging
} catch (error) {
// Handle any errors during the file upload process
console.log('Error Uploading File: ', error.response?.data || error.message);
}
};The sendQuestion function sends a question to the backend and retrieves the answer.
const sendQuestion = async (inputValue) => {
// Check if a file is uploaded
if (!uploadedFileName) {
alert("Please upload the file first!"); // Alert the user if no file is uploaded
return;
}
try {
// Send the user's question and filename to the backend via POST request
const response = await axios.post("http://localhost:8000/ask/", {
filename: uploadedFileName,
question: inputValue,
});
console.log("Response: ", response.data); // Log the response for debugging
setAnswer(response.data.answer); // Set the AI's answer using the `setAnswer` function
} catch (error) {
// Log any error that occurs during the request
console.log("Error in sending message: ", error.response?.data || error.message);
}
};- URL:
/upload/ - Method:
POST - Payload:
multipart/form-datacontaining the file. - Response:
{ "message": "File uploaded and processed successfully.", "filename": "example.pdf" } - URL:
/ask/ - Method:
POST - Payload:
- filename: The name of the uploaded PDF file to query.
- question: The question to ask based on the file's content.
{ "filename": "example.pdf", "question": "What is the content of the first page?" }
- FastAPI: Framework for building APIs.
- PyMuPDF: Library for working with PDF files.
- pydantic: Data validation and settings management.
- Groq client: Interface for interacting with the Llama AI model.
- React.js: Frontend library for building user interfaces.
- Axios: HTTP client for making API requests.