Javedalam commited on
Commit
2210de6
Β·
verified Β·
1 Parent(s): e7147b0

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +135 -0
app.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, tempfile, traceback
2
+ import gradio as gr
3
+ import spaces
4
+ import requests
5
+
6
+ # ---------- Cache & HF Hub settings (before importing Docling) ----------
7
+ # Use persistent storage (/data) and disable xet (fixes PermissionDenied in some Spaces)
8
+ os.environ.setdefault("HF_HOME", "/data/.cache/huggingface")
9
+ os.environ.setdefault("HF_HUB_CACHE", "/data/.cache/huggingface/hub")
10
+ os.environ.setdefault("TRANSFORMERS_CACHE", "/data/.cache/huggingface/transformers")
11
+ os.environ.setdefault("HF_HUB_ENABLE_XET", "0") # avoid xet write issues
12
+ os.environ.setdefault("HF_HUB_ENABLE_HF_TRANSFER", "1") # faster downloads
13
+ os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
14
+
15
+ # Make sure the folders exist
16
+ for p in (os.environ["HF_HOME"], os.environ["HF_HUB_CACHE"], os.environ["TRANSFORMERS_CACHE"]):
17
+ os.makedirs(p, exist_ok=True)
18
+
19
+ # ---------- Imports after env is set ----------
20
+ from docling.datamodel.base_models import InputFormat
21
+ from docling.document_converter import DocumentConverter, PdfFormatOption
22
+ from docling.pipeline.vlm_pipeline import VlmPipeline
23
+
24
+ # Detect CUDA (ZeroGPU will make this true on first decorated call)
25
+ try:
26
+ import torch
27
+ HAS_CUDA = torch.cuda.is_available()
28
+ # keep threads modest on shared infra
29
+ torch.set_num_threads(max(1, int(os.environ.get("OMP_NUM_THREADS", "2"))))
30
+ except Exception:
31
+ HAS_CUDA = False
32
+
33
+ # Build converters once (lifetime of app)
34
+ # Standard = text-first (faster, good when PDF has text layer)
35
+ std_converter = DocumentConverter(
36
+ format_options={InputFormat.PDF: PdfFormatOption()}
37
+ )
38
+ # VLM = Granite Docling (better for scans/tables/math)
39
+ vlm_converter = DocumentConverter(
40
+ format_options={InputFormat.PDF: PdfFormatOption(pipeline_cls=VlmPipeline)}
41
+ )
42
+
43
+ # ---------- Helpers ----------
44
+ def _success(md: str, html: str):
45
+ tmpdir = tempfile.gettempdir()
46
+ md_path = os.path.join(tmpdir, "output.md")
47
+ html_path = os.path.join(tmpdir, "output.html")
48
+ with open(md_path, "w", encoding="utf-8") as f:
49
+ f.write(md)
50
+ with open(html_path, "w", encoding="utf-8") as f:
51
+ f.write(html)
52
+ return md, md_path, html_path
53
+
54
+ def _fail(msg: str):
55
+ # show readable error in the preview panel
56
+ return f"**Conversion failed**:\n```\n{msg}\n```", None, None
57
+
58
+ def _convert_local_path(path: str, use_vlm: bool):
59
+ try:
60
+ conv = vlm_converter if use_vlm else std_converter
61
+ doc = conv.convert(source=path).document
62
+ md = doc.export_to_markdown()
63
+ html = doc.export_to_html()
64
+ return _success(md, html)
65
+ except Exception as e:
66
+ return _fail(f"{e}\n\n{traceback.format_exc()}")
67
+
68
+ # ---------- GPU-decorated endpoints (ZeroGPU requirement) ----------
69
+ @spaces.GPU(duration=600) # up to 10 minutes
70
+ def run_convert_file(file, mode):
71
+ if file is None:
72
+ return _fail("No file provided.")
73
+ use_vlm = mode.startswith("VLM")
74
+ return _convert_local_path(file.name, use_vlm)
75
+
76
+ @spaces.GPU(duration=600)
77
+ def run_convert_url(url, mode):
78
+ if not url:
79
+ return _fail("No URL provided.")
80
+ # download to a temp file so Docling always reads a local path
81
+ try:
82
+ r = requests.get(url, stream=True, timeout=60)
83
+ r.raise_for_status()
84
+ fd, tmp_path = tempfile.mkstemp(suffix=".pdf")
85
+ with os.fdopen(fd, "wb") as tmp:
86
+ for chunk in r.iter_content(chunk_size=1 << 20):
87
+ if chunk:
88
+ tmp.write(chunk)
89
+ except Exception as e:
90
+ return _fail(f"Failed to download URL: {e}")
91
+ try:
92
+ return _convert_local_path(tmp_path, mode.startswith("VLM"))
93
+ finally:
94
+ try:
95
+ os.remove(tmp_path)
96
+ except Exception:
97
+ pass
98
+
99
+ # ---------- UI ----------
100
+ subtitle = "Device: **CUDA (ZeroGPU)**" if HAS_CUDA else "Device: **CPU** (GPU warms on first call)"
101
+
102
+ with gr.Blocks(title="Granite-Docling 258M β€” PDF β†’ Markdown/HTML") as demo:
103
+ gr.Markdown(
104
+ f"""# Granite-Docling 258M β€” PDF β†’ Markdown / HTML
105
+ {subtitle}
106
+
107
+ **Modes**
108
+ - **Standard (faster)** β†’ PDFs with a text layer
109
+ - **VLM (Granite – better for complex/scanned)** β†’ scans / heavy tables / formulas
110
+
111
+ _First call may be slow while models download and ZeroGPU warms. Cache lives in `/data`._
112
+ """
113
+ )
114
+
115
+ mode = gr.Radio(
116
+ ["Standard (faster)", "VLM (Granite – better for complex/scanned)"],
117
+ value="Standard (faster)", label="Mode"
118
+ )
119
+
120
+ with gr.Tab("Upload PDF"):
121
+ fi = gr.File(file_types=[".pdf"], label="PDF")
122
+ out_md = gr.Markdown(label="Markdown Preview")
123
+ dl_md = gr.File(label="Download Markdown")
124
+ dl_html = gr.File(label="Download HTML")
125
+ gr.Button("Convert").click(run_convert_file, [fi, mode], [out_md, dl_md, dl_html])
126
+
127
+ with gr.Tab("Convert from URL"):
128
+ url = gr.Textbox(label="Public PDF URL", placeholder="https://.../file.pdf")
129
+ out2_md = gr.Markdown(label="Markdown Preview")
130
+ dl2_md = gr.File(label="Download Markdown")
131
+ dl2_html = gr.File(label="Download HTML")
132
+ gr.Button("Convert").click(run_convert_url, [url, mode], [out2_md, dl2_md, dl2_html])
133
+
134
+ # Explicit bind & queue
135
+ demo.queue().launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))