Spaces:
Sleeping
Sleeping
Upload 3 files
Browse filesUpdated app.py files by adding OCR Recognition Toggle bar
- DEPLOYMENT.md +8 -3
- README.md +1 -1
- app.py +20 -3
DEPLOYMENT.md
CHANGED
|
@@ -4,7 +4,7 @@ This guide covers deploying DocuChat_AI as a Streamlit app on Hugging Face Space
|
|
| 4 |
|
| 5 |
## 1. Recommended Hugging Face Setup
|
| 6 |
|
| 7 |
-
For the standard app:
|
| 8 |
|
| 9 |
```text
|
| 10 |
SDK: Streamlit
|
|
@@ -48,7 +48,7 @@ Pillow
|
|
| 48 |
Tesseract OCR binary
|
| 49 |
```
|
| 50 |
|
| 51 |
-
Python packages are installed from `requirements.txt`, but the Tesseract system binary
|
| 52 |
|
| 53 |
For best OCR support, deploy using the included `Dockerfile`, which installs:
|
| 54 |
|
|
@@ -66,6 +66,12 @@ Hardware: CPU Upgrade if processing larger scanned PDFs
|
|
| 66 |
|
| 67 |
If OCR dependencies are missing, the app fails gracefully and still works for normal text-based PDFs, DOCX, and TXT files.
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
## 3. Evaluation Dashboard
|
| 70 |
|
| 71 |
The Evaluation tab supports labeled RAG testing with CSV files.
|
|
@@ -117,4 +123,3 @@ The correctness and faithfulness scores are LLM-judged and should be treated as
|
|
| 117 |
- Add `APP_PASSWORD` for portfolio demos shared with recruiters.
|
| 118 |
- Use the Evaluation tab with a small labeled test set before demos.
|
| 119 |
- Use CPU Upgrade for larger PDFs or frequent OCR use.
|
| 120 |
-
|
|
|
|
| 4 |
|
| 5 |
## 1. Recommended Hugging Face Setup
|
| 6 |
|
| 7 |
+
For the standard app without scanned PDF OCR:
|
| 8 |
|
| 9 |
```text
|
| 10 |
SDK: Streamlit
|
|
|
|
| 48 |
Tesseract OCR binary
|
| 49 |
```
|
| 50 |
|
| 51 |
+
Python packages are installed from `requirements.txt`, but the Tesseract system binary is usually not available in a normal Streamlit SDK Space.
|
| 52 |
|
| 53 |
For best OCR support, deploy using the included `Dockerfile`, which installs:
|
| 54 |
|
|
|
|
| 66 |
|
| 67 |
If OCR dependencies are missing, the app fails gracefully and still works for normal text-based PDFs, DOCX, and TXT files.
|
| 68 |
|
| 69 |
+
Important:
|
| 70 |
+
|
| 71 |
+
```text
|
| 72 |
+
If your scanned PDF returns "No readable text found", switch the Space SDK to Docker or create a new Docker Space using this repository.
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
## 3. Evaluation Dashboard
|
| 76 |
|
| 77 |
The Evaluation tab supports labeled RAG testing with CSV files.
|
|
|
|
| 123 |
- Add `APP_PASSWORD` for portfolio demos shared with recruiters.
|
| 124 |
- Use the Evaluation tab with a small labeled test set before demos.
|
| 125 |
- Use CPU Upgrade for larger PDFs or frequent OCR use.
|
|
|
README.md
CHANGED
|
@@ -267,7 +267,7 @@ GROQ_API_KEY = your_key_here
|
|
| 267 |
5. Optional: add `APP_PASSWORD` in **Settings -> Secrets** to protect the app.
|
| 268 |
6. Restart the Space after dependency changes.
|
| 269 |
|
| 270 |
-
For scanned PDF OCR, use the included Dockerfile because OCR needs system
|
| 271 |
|
| 272 |
See [DEPLOYMENT.md](DEPLOYMENT.md) for the full deployment guide.
|
| 273 |
|
|
|
|
| 267 |
5. Optional: add `APP_PASSWORD` in **Settings -> Secrets** to protect the app.
|
| 268 |
6. Restart the Space after dependency changes.
|
| 269 |
|
| 270 |
+
For scanned PDF OCR, use the included Dockerfile because OCR needs the Tesseract system binary. Normal Streamlit SDK Spaces may not include it.
|
| 271 |
|
| 272 |
See [DEPLOYMENT.md](DEPLOYMENT.md) for the full deployment guide.
|
| 273 |
|
app.py
CHANGED
|
@@ -390,6 +390,9 @@ def ocr_pdf_pages(pdf_path: str, source_name: str, max_pages: int = 8) -> list:
|
|
| 390 |
)
|
| 391 |
return docs
|
| 392 |
|
|
|
|
|
|
|
|
|
|
| 393 |
def normalize_source_metadata(docs: list, source_name: str, file_type: str, extraction: str = "text") -> list:
|
| 394 |
for doc in docs:
|
| 395 |
doc.metadata["source"] = source_name
|
|
@@ -421,6 +424,7 @@ def load_documents(files, use_ocr: bool = False, ocr_page_limit: int = 8) -> lis
|
|
| 421 |
if extracted_chars < 250:
|
| 422 |
st.write(f"π Running OCR for scanned PDF: {file.name}")
|
| 423 |
loaded = ocr_pdf_pages(temp_path, file.name, max_pages=ocr_page_limit)
|
|
|
|
| 424 |
docs.extend(loaded[:MAX_PAGES])
|
| 425 |
except Exception as e:
|
| 426 |
st.error(f"β οΈ Error loading `{file.name}`: {e}")
|
|
@@ -989,8 +993,6 @@ with st.sidebar:
|
|
| 989 |
with st.expander("βοΈ Advanced Settings"):
|
| 990 |
temperature = st.slider("Temperature", 0.0, 1.0, 0.3, 0.05)
|
| 991 |
top_k = st.slider("Retrieved Chunks (Top-K)", 2, 10, 4)
|
| 992 |
-
use_ocr = st.checkbox("Enable OCR fallback for scanned PDFs", value=False)
|
| 993 |
-
ocr_page_limit = st.slider("OCR Page Limit", 1, 25, 8)
|
| 994 |
|
| 995 |
st.divider()
|
| 996 |
|
|
@@ -1001,6 +1003,11 @@ with st.sidebar:
|
|
| 1001 |
accept_multiple_files=True,
|
| 1002 |
)
|
| 1003 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1004 |
col1, col2 = st.columns(2)
|
| 1005 |
process_btn = col1.button("π Process", type="primary", use_container_width=True)
|
| 1006 |
summarize_btn = col2.button("π Summary", use_container_width=True)
|
|
@@ -1025,8 +1032,11 @@ with st.sidebar:
|
|
| 1025 |
st.write("π₯ Loading files into memory...")
|
| 1026 |
raw_docs = load_documents(uploaded_files, use_ocr=use_ocr, ocr_page_limit=ocr_page_limit)
|
| 1027 |
|
| 1028 |
-
if not raw_docs:
|
| 1029 |
status.update(label="No content found!", state="error")
|
|
|
|
|
|
|
|
|
|
| 1030 |
st.stop()
|
| 1031 |
|
| 1032 |
# BUG FIX: Save the full raw text into session state for the summary function
|
|
@@ -1040,6 +1050,13 @@ with st.sidebar:
|
|
| 1040 |
)
|
| 1041 |
chunks = splitter.split_documents(raw_docs)
|
| 1042 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1043 |
st.write("π§ Generating embeddings...")
|
| 1044 |
embeddings = get_embeddings()
|
| 1045 |
|
|
|
|
| 390 |
)
|
| 391 |
return docs
|
| 392 |
|
| 393 |
+
def has_readable_text(docs: list) -> bool:
|
| 394 |
+
return any(doc.page_content and doc.page_content.strip() for doc in docs)
|
| 395 |
+
|
| 396 |
def normalize_source_metadata(docs: list, source_name: str, file_type: str, extraction: str = "text") -> list:
|
| 397 |
for doc in docs:
|
| 398 |
doc.metadata["source"] = source_name
|
|
|
|
| 424 |
if extracted_chars < 250:
|
| 425 |
st.write(f"π Running OCR for scanned PDF: {file.name}")
|
| 426 |
loaded = ocr_pdf_pages(temp_path, file.name, max_pages=ocr_page_limit)
|
| 427 |
+
loaded = [doc for doc in loaded if doc.page_content and doc.page_content.strip()]
|
| 428 |
docs.extend(loaded[:MAX_PAGES])
|
| 429 |
except Exception as e:
|
| 430 |
st.error(f"β οΈ Error loading `{file.name}`: {e}")
|
|
|
|
| 993 |
with st.expander("βοΈ Advanced Settings"):
|
| 994 |
temperature = st.slider("Temperature", 0.0, 1.0, 0.3, 0.05)
|
| 995 |
top_k = st.slider("Retrieved Chunks (Top-K)", 2, 10, 4)
|
|
|
|
|
|
|
| 996 |
|
| 997 |
st.divider()
|
| 998 |
|
|
|
|
| 1003 |
accept_multiple_files=True,
|
| 1004 |
)
|
| 1005 |
|
| 1006 |
+
st.subheader("π Scanned PDF OCR")
|
| 1007 |
+
use_ocr = st.toggle("Enable OCR for scanned PDFs", value=True, help="Use this for image-based PDFs that have no selectable text.")
|
| 1008 |
+
ocr_page_limit = st.slider("OCR page limit", 1, 25, 8, help="Higher values are slower on CPU Spaces.")
|
| 1009 |
+
st.caption("OCR needs Tesseract. Use Docker Space for the most reliable scanned PDF support.")
|
| 1010 |
+
|
| 1011 |
col1, col2 = st.columns(2)
|
| 1012 |
process_btn = col1.button("π Process", type="primary", use_container_width=True)
|
| 1013 |
summarize_btn = col2.button("π Summary", use_container_width=True)
|
|
|
|
| 1032 |
st.write("π₯ Loading files into memory...")
|
| 1033 |
raw_docs = load_documents(uploaded_files, use_ocr=use_ocr, ocr_page_limit=ocr_page_limit)
|
| 1034 |
|
| 1035 |
+
if not raw_docs or not has_readable_text(raw_docs):
|
| 1036 |
status.update(label="No content found!", state="error")
|
| 1037 |
+
st.error(
|
| 1038 |
+
"No readable text was found. If this is a scanned PDF, keep OCR enabled and deploy with Docker so Tesseract OCR is installed."
|
| 1039 |
+
)
|
| 1040 |
st.stop()
|
| 1041 |
|
| 1042 |
# BUG FIX: Save the full raw text into session state for the summary function
|
|
|
|
| 1050 |
)
|
| 1051 |
chunks = splitter.split_documents(raw_docs)
|
| 1052 |
|
| 1053 |
+
if not chunks:
|
| 1054 |
+
status.update(label="No searchable chunks created!", state="error")
|
| 1055 |
+
st.error(
|
| 1056 |
+
"The document loaded, but no searchable text chunks were created. For scanned PDFs, enable OCR and use Docker deployment with Tesseract."
|
| 1057 |
+
)
|
| 1058 |
+
st.stop()
|
| 1059 |
+
|
| 1060 |
st.write("π§ Generating embeddings...")
|
| 1061 |
embeddings = get_embeddings()
|
| 1062 |
|