forked from madhavarora1988/MistralOCR
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
189 lines (163 loc) · 7.3 KB
/
Copy pathapp.py
File metadata and controls
189 lines (163 loc) · 7.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import streamlit as st
import tempfile
import os
from pathlib import Path
from mistralai import Mistral
from mistralai import DocumentURLChunk, ImageURLChunk, TextChunk # TextChunk not used in this snippet but good to keep if Mistral changes
from mistralai.models import OCRResponse
import json # Not explicitly used in this snippet, can be removed if not needed elsewhere
from dotenv import load_dotenv
# Load environment variables from .env file (local development)
load_dotenv()
# Get API key from environment variables (works for both local and cloud deployment)
api_key = os.getenv('MISTRAL_API_KEY')
if not api_key:
st.error("""
Missing MISTRAL_API_KEY in environment variables.
Local users: Please check your .env file
Cloud deployment: Ensure you've set up the secret in Streamlit Cloud
""")
st.stop()
# Initialize Mistral client
client = Mistral(api_key=api_key)
def replace_images_in_markdown(markdown_str: str, images_dict: dict) -> str:
"""Replace image placeholders with base64 encoded images in markdown."""
for img_name, base64_str in images_dict.items():
markdown_str = markdown_str.replace(f"", f"")
return markdown_str
def get_combined_markdown(ocr_response: OCRResponse) -> str:
"""Combine markdown from all pages with their respective images."""
markdowns: list[str] = []
for page in ocr_response.pages:
image_data = {}
for img in page.images:
image_data[img.id] = img.image_base64
markdowns.append(replace_images_in_markdown(page.markdown, image_data))
return "\n\n".join(markdowns)
def process_file_to_markdown(file_path: str, file_type_for_api: str) -> str:
"""
Convert PDF or image file to Markdown using Mistral OCR API
Args:
file_path (str): Path to the temporary file
file_type_for_api (str): Type of file for API call ('pdf' or 'image')
Returns:
str: Converted markdown text
"""
try:
# Convert string path to Path object
file = Path(file_path)
# Upload file to Mistral
uploaded_file_mistral = client.files.upload(
file={
"file_name": file.stem, # Use stem of the temp file for Mistral's reference
"content": file.read_bytes(),
},
purpose="ocr",
)
# Get signed URL
signed_url = client.files.get_signed_url(file_id=uploaded_file_mistral.id, expiry=1) # Short expiry as it's used immediately
# Process the file with OCR
if file_type_for_api == 'pdf':
response = client.ocr.process(
document=DocumentURLChunk(document_url=signed_url.url),
model="mistral-ocr-latest",
include_image_base64=True
)
else: # image
response = client.ocr.process(
document=ImageURLChunk(image_url=signed_url.url),
model="mistral-ocr-latest",
include_image_base64=True
)
# Convert to markdown
markdown_text = get_combined_markdown(response)
return markdown_text
except Exception as e:
st.error(f"Error in file conversion: {str(e)}")
return None
def main():
st.title("Document to Markdown Converter")
st.write("Convert a PDF, image file, or camera capture to Markdown format.")
# Input method selector
input_method = st.radio(
"Choose an input method:",
("Upload PDF", "Upload Image File", "Take a Picture (Camera)"),
horizontal=True,
key="input_method_selector"
)
uploaded_file_streamlit = None # To store the file object from Streamlit uploader/camera
file_type_for_processing = None # This will be 'pdf' or 'image' for process_file_to_markdown
if input_method == "Upload PDF":
uploaded_file_streamlit = st.file_uploader(
"Choose a PDF file",
type=['pdf'],
key="pdf_uploader"
)
if uploaded_file_streamlit:
file_type_for_processing = 'pdf'
elif input_method == "Upload Image File":
uploaded_file_streamlit = st.file_uploader(
"Choose an image file",
type=['png', 'jpg', 'jpeg', 'tiff', 'bmp'],
key="image_uploader"
)
if uploaded_file_streamlit:
file_type_for_processing = 'image'
elif input_method == "Take a Picture (Camera)":
# camera_input returns an UploadedFile object or None
camera_photo = st.camera_input("Take a picture", key="camera_input")
if camera_photo:
uploaded_file_streamlit = camera_photo
file_type_for_processing = 'image' # Camera always produces an image
if uploaded_file_streamlit is not None and file_type_for_processing is not None:
# Determine file extension for temp file
original_file_name = uploaded_file_streamlit.name # e.g., "my_doc.pdf" or "camera_input.png"
file_extension = Path(original_file_name).suffix
# Robust fallback for extension if name doesn't have one
if not file_extension:
if uploaded_file_streamlit.type == "application/pdf":
file_extension = ".pdf"
elif uploaded_file_streamlit.type == "image/png":
file_extension = ".png"
elif uploaded_file_streamlit.type == "image/jpeg":
file_extension = ".jpg"
elif uploaded_file_streamlit.type == "image/bmp":
file_extension = ".bmp"
elif uploaded_file_streamlit.type == "image/tiff":
file_extension = ".tiff"
else:
st.warning(f"Could not reliably determine file extension for {original_file_name} (type: {uploaded_file_streamlit.type}). Using '.tmp'.")
file_extension = ".tmp" # Generic extension if all else fails
# Create a temporary file to store the uploaded content
with tempfile.NamedTemporaryFile(delete=False, suffix=file_extension) as tmp_file:
tmp_file.write(uploaded_file_streamlit.getvalue())
tmp_file_path = tmp_file.name
try:
with st.spinner('Converting to Markdown...'):
# Convert file to Markdown
markdown_text = process_file_to_markdown(
tmp_file_path,
file_type_for_processing
)
if markdown_text:
st.subheader("Converted Markdown:")
st.markdown(markdown_text)
st.subheader("Raw Markdown:")
st.text_area("Raw Markdown Output", markdown_text, height=300)
# Add a download button for the markdown
download_file_name = f"{Path(original_file_name).stem}_converted.md"
st.download_button(
label="Download Markdown",
data=markdown_text,
file_name=download_file_name,
mime="text/markdown"
)
else:
st.error("Conversion failed. Please check the file or camera input.")
except Exception as e:
st.error(f"An error occurred: {str(e)}")
finally:
# Clean up the temporary file
os.unlink(tmp_file_path)
if __name__ == "__main__":
main()