Charles Azam
feat: add pdf agent
8dfc9f8
raw
history blame
4.66 kB
import os
from pathlib import Path
from pypdf import PdfReader, PdfWriter
import io
from pathlib import Path
from mistralai import Mistral
import os
from litellm import completion
from mistralai.models import OCRResponse, OCRPageObject
import yaml
from tenacity import retry, stop_after_attempt, wait_fixed, RetryError
from litellm.exceptions import BadRequestError
# Define the size limit in bytes
MAX_SIZE_BYTES = 49 * 1024 * 1024
async def convert_pdf_to_markdown_async(
pdf_path: Path,
with_image_description: bool = False,
) -> tuple[OCRResponse]:
mistral_client = Mistral(api_key=os.getenv("MISTRAL_API_KEY"))
uploaded_pdf = await mistral_client.files.upload_async(
file={
"file_name": "uploaded_file.pdf",
"content": open(pdf_path, "rb"),
},
purpose="ocr",
)
signed_url = await mistral_client.files.get_signed_url_async(file_id=uploaded_pdf.id)
ocr_response = await mistral_client.ocr.process_async(
model="mistral-ocr-latest",
document={"type": "document_url", "document_url": signed_url.url},
include_image_base64=True,
)
print(f"Processing PDF: {pdf_path.name}")
return ocr_response
def convert_ocr_response_to_markdown(
ocr_response: OCRResponse
) -> str:
markdowns: list[str] = []
for page in ocr_response.pages:
page_description = page.markdown
markdowns.append(page_description)
return "\n\n".join(markdowns)
def get_markdown_by_page_numbers(markdown: OCRResponse, page_numbers: list[int], get_full_content: bool = False) -> str:
markdowns: list[str] = []
page_numbers_to_get = set(page_numbers)
if get_full_content:
page_numbers_to_get = set(range(len(markdown.pages)))
for page_number in page_numbers_to_get:
markdowns.append(f"*Page {page_number}*\n{markdown.pages[page_number].markdown}")
return "\n\n".join(markdowns)
def find_in_pdf(markdown: OCRResponse, search_queries: list[str]) -> list[int]:
"""
Find the page numbers of the pdf that contain the search query.
Args:
markdown (OCRResponse): The markdown of the pdf.
search_queries (list[str]): The search queries.
Returns:
list[int]: The page numbers of the pdf that contain the search query.
"""
page_numbers: list[int] = []
for page_number, page in enumerate(markdown.pages):
for search_query in search_queries:
if search_query.lower() in page.markdown.lower():
page_numbers.append(page_number)
return page_numbers
def get_table_of_contents_per_page_pdf(markdown: OCRResponse) -> str:
"""
Get the table of contents of the pdf.
Finds all the titles of the pdf to reconstruct the table of contents.
Args:
markdown (OCRResponse): The markdown of the pdf.
Returns:
str: The table of contents of the pdf.
"""
title_to_page_number: dict[str, int] = {}
for page_number, page in enumerate(markdown.pages):
lines = page.markdown.split("\n")
for line in lines:
line = line.strip()
if line.startswith("#"):
title_to_page_number[line] = page_number
table_of_contents = "\n".join([f"{title} - Page {page_number}" for title, page_number in title_to_page_number.items()])
return table_of_contents
def convert_raw_markdown_to_ocr_response(raw_markdown: str) -> OCRResponse:
# split by big title starting with # and then a space
pages = raw_markdown.split("\n# ")
return OCRResponse(pages=[OCRPageObject(markdown="# " + page, page_number=i) for i, page in enumerate(pages)])
def get_images_from_pdf(pdf_path: Path, image_ids: list[str]) -> list[str]:
raise NotImplementedError("Not implemented")
def get_image_description_using_llm(
base_64_str: str, model: str = "mistral/mistral-small-latest"
) -> str | None:
assert base_64_str.startswith("data:image/jpeg;base64")
messages = [
{
"role": "user",
"content": [
{"type": "text", "text": "Describe this image in detail:"},
{"type": "image_url", "image_url": {"url": base_64_str}},
],
}
]
try:
response = completion(
model=model, # LiteLLM naming convention
messages=messages,
temperature=0.0,
stream=False,
)
output = dict(response)["choices"][0].message.content
except BadRequestError:
output = ""
return output