rehan953 commited on
Commit
14a7432
·
verified ·
1 Parent(s): 721c9d9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -39
app.py CHANGED
@@ -1,87 +1,184 @@
1
  #!/usr/bin/env python3
2
 
3
- import asyncio
4
  import logging
5
  import os
6
- from typing import Any
 
 
7
 
8
- try:
9
- _orig_close = asyncio.BaseEventLoop.close
 
10
 
11
- def _safe_close(self):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  try:
13
- _orig_close(self)
14
- except (ValueError, OSError):
15
- pass
 
 
 
 
 
16
 
17
- asyncio.BaseEventLoop.close = _safe_close
18
- except Exception:
19
- pass
20
 
21
- log = logging.getLogger("glmocr_raw_app")
22
- logging.basicConfig(level=logging.INFO)
23
 
24
- GLMOCR_API_KEY = "cee1d52dd91a4ab591b3f6e105f8ad89.LgbQTECuzX0zrito"
 
 
25
 
26
- _parser = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
 
29
  def get_parser():
30
  global _parser
31
  if _parser is None:
 
 
32
  from glmocr import GlmOcr
33
 
34
- kwargs = {"mode": "maas"}
35
- if GLMOCR_API_KEY:
36
- kwargs["api_key"] = GLMOCR_API_KEY
37
- _parser = GlmOcr(**kwargs)
38
  return _parser
39
 
40
 
41
- def _extract_markdown(result: Any) -> str:
42
  if result is None:
43
  return ""
44
  if isinstance(result, list):
45
- parts = []
46
  for item in result:
47
  md = getattr(item, "markdown_result", "")
48
  if md:
49
- parts.append(str(md).strip())
50
- return "\n\n---page-separator---\n\n".join(p for p in parts if p).strip()
51
  md = getattr(result, "markdown_result", "")
52
  return str(md).strip() if md else ""
53
 
54
 
55
- def run_ocr_raw(uploaded_file):
56
- if uploaded_file is None:
57
- return "Please upload a file."
 
 
 
 
 
 
 
 
 
 
58
  try:
59
- path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)
60
  parser = get_parser()
61
  result = parser.parse(path)
62
- markdown = _extract_markdown(result)
63
- return markdown or "(No content)"
 
64
  except Exception as e:
65
  import traceback
66
 
67
- log.exception("run_ocr_raw failed: %s", e)
68
- return f"Error: {e}\n\n{traceback.format_exc()}"
69
 
70
 
71
- def _create_gradio_demo():
72
- import gradio as gr
73
-
74
- with gr.Blocks(title="GLM-OCR Raw (no fixes)") as demo:
75
- gr.Markdown("# GLM-OCR Raw (No Fixes)")
 
 
76
  file_in = gr.File(
77
  label="Upload PDF or image",
78
  file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp"],
79
  )
80
  run_btn = gr.Button("Run OCR", variant="primary")
81
- out = gr.Textbox(lines=40, label="Raw markdown_result")
82
- run_btn.click(fn=run_ocr_raw, inputs=file_in, outputs=out)
 
83
  return demo
84
 
85
 
86
  if __name__ == "__main__":
87
- _create_gradio_demo().launch()
 
1
  #!/usr/bin/env python3
2
 
3
+ import json
4
  import logging
5
  import os
6
+ import subprocess
7
+ import time
8
+ from typing import Any, List
9
 
10
+ import gradio as gr
11
+ import httpx
12
+ import yaml
13
 
14
+ log = logging.getLogger("glmocr_selfhosted_space")
15
+ logging.basicConfig(level=logging.INFO)
16
+
17
+ MODEL_ID = os.getenv("GLMOCR_MODEL_ID", "zai-org/GLM-OCR")
18
+ OCR_API_PORT = int(os.getenv("OCR_API_PORT", "8080"))
19
+ OCR_API_BASE = f"http://127.0.0.1:{OCR_API_PORT}"
20
+ VLLM_MAX_MODEL_LEN = os.getenv("VLLM_MAX_MODEL_LEN", "8192")
21
+ VLLM_GPU_MEMORY_UTIL = os.getenv("VLLM_GPU_MEMORY_UTIL", "0.92")
22
+ VLLM_EXTRA_ARGS = os.getenv("VLLM_EXTRA_ARGS", "").strip()
23
+
24
+ _parser = None
25
+ _vllm_proc = None
26
+
27
+
28
+ def _start_vllm_if_needed() -> None:
29
+ global _vllm_proc
30
+ if _vllm_proc is not None and _vllm_proc.poll() is None:
31
+ return
32
+
33
+ cmd: List[str] = [
34
+ "python",
35
+ "-m",
36
+ "vllm.entrypoints.openai.api_server",
37
+ "--model",
38
+ MODEL_ID,
39
+ "--served-model-name",
40
+ "glm-ocr",
41
+ "--port",
42
+ str(OCR_API_PORT),
43
+ "--max-model-len",
44
+ VLLM_MAX_MODEL_LEN,
45
+ "--gpu-memory-utilization",
46
+ VLLM_GPU_MEMORY_UTIL,
47
+ ]
48
+ if VLLM_EXTRA_ARGS:
49
+ cmd.extend(VLLM_EXTRA_ARGS.split())
50
+
51
+ log.info("Starting vLLM server: %s", " ".join(cmd))
52
+ _vllm_proc = subprocess.Popen(cmd)
53
+
54
+ deadline = time.time() + 420
55
+ last_err: str = ""
56
+ while time.time() < deadline:
57
+ if _vllm_proc.poll() is not None:
58
+ raise RuntimeError("vLLM server exited early. Check Space logs for details.")
59
  try:
60
+ resp = httpx.get(f"{OCR_API_BASE}/v1/models", timeout=8.0)
61
+ if resp.status_code == 200:
62
+ log.info("vLLM is ready.")
63
+ return
64
+ except Exception as e:
65
+ last_err = str(e)
66
+ time.sleep(3)
67
+ raise RuntimeError(f"Timed out waiting for vLLM startup. Last error: {last_err}")
68
 
 
 
 
69
 
70
+ def _configure_glmocr_to_keep_header_footer() -> None:
71
+ import glmocr
72
 
73
+ config_path = os.path.join(os.path.dirname(glmocr.__file__), "config.yaml")
74
+ with open(config_path, "r", encoding="utf-8") as f:
75
+ cfg = yaml.safe_load(f) or {}
76
 
77
+ pipeline = cfg.setdefault("pipeline", {})
78
+
79
+ maas = pipeline.setdefault("maas", {})
80
+ maas["enabled"] = False
81
+
82
+ ocr_api = pipeline.setdefault("ocr_api", {})
83
+ ocr_api["api_host"] = "127.0.0.1"
84
+ ocr_api["api_port"] = OCR_API_PORT
85
+ ocr_api["model"] = "glm-ocr"
86
+ ocr_api["api_path"] = "/v1/chat/completions"
87
+ ocr_api["api_mode"] = "openai"
88
+ ocr_api["verify_ssl"] = False
89
+
90
+ layout = pipeline.setdefault("layout", {})
91
+ label_task_mapping = layout.setdefault("label_task_mapping", {})
92
+ text_labels = set(label_task_mapping.get("text", []) or [])
93
+ abandon_labels = set(label_task_mapping.get("abandon", []) or [])
94
+ skip_labels = set(label_task_mapping.get("skip", []) or [])
95
+
96
+ text_labels.update({"header", "footer"})
97
+ abandon_labels.discard("header")
98
+ abandon_labels.discard("footer")
99
+
100
+ # Keep image-only decorative zones out of OCR by default.
101
+ # If you want logo OCR too, move these to text_labels.
102
+ skip_labels.update({"header_image", "footer_image"})
103
+ abandon_labels.discard("header_image")
104
+ abandon_labels.discard("footer_image")
105
+
106
+ label_task_mapping["text"] = sorted(text_labels)
107
+ label_task_mapping["abandon"] = sorted(abandon_labels)
108
+ label_task_mapping["skip"] = sorted(skip_labels)
109
+
110
+ with open(config_path, "w", encoding="utf-8") as f:
111
+ yaml.safe_dump(cfg, f, sort_keys=False)
112
 
113
 
114
  def get_parser():
115
  global _parser
116
  if _parser is None:
117
+ _start_vllm_if_needed()
118
+ _configure_glmocr_to_keep_header_footer()
119
  from glmocr import GlmOcr
120
 
121
+ _parser = GlmOcr()
 
 
 
122
  return _parser
123
 
124
 
125
+ def _extract_md(result: Any) -> str:
126
  if result is None:
127
  return ""
128
  if isinstance(result, list):
129
+ chunks = []
130
  for item in result:
131
  md = getattr(item, "markdown_result", "")
132
  if md:
133
+ chunks.append(str(md).strip())
134
+ return "\n\n---page-separator---\n\n".join([c for c in chunks if c]).strip()
135
  md = getattr(result, "markdown_result", "")
136
  return str(md).strip() if md else ""
137
 
138
 
139
+ def _extract_json(result: Any) -> str:
140
+ if result is None:
141
+ return "{}"
142
+ if isinstance(result, list):
143
+ payload = [getattr(item, "json_result", None) for item in result]
144
+ return json.dumps(payload, ensure_ascii=False, indent=2)
145
+ return json.dumps(getattr(result, "json_result", None), ensure_ascii=False, indent=2)
146
+
147
+
148
+ def run_ocr(file_obj):
149
+ if file_obj is None:
150
+ return "Please upload a file.", "{}"
151
+ path = file_obj.name if hasattr(file_obj, "name") else str(file_obj)
152
  try:
 
153
  parser = get_parser()
154
  result = parser.parse(path)
155
+ md = _extract_md(result) or "(No content)"
156
+ jr = _extract_json(result)
157
+ return md, jr
158
  except Exception as e:
159
  import traceback
160
 
161
+ log.exception("run_ocr failed: %s", e)
162
+ return f"Error: {e}\n\n{traceback.format_exc()}", "{}"
163
 
164
 
165
+ def build_demo():
166
+ with gr.Blocks(title="GLM-OCR Self-hosted (Header/Footer enabled)") as demo:
167
+ gr.Markdown("# GLM-OCR Self-hosted (Header/Footer enabled)")
168
+ gr.Markdown(
169
+ "Runs a local vLLM server inside the Space and configures GLM-OCR "
170
+ "to include `header` and `footer` regions in OCR output."
171
+ )
172
  file_in = gr.File(
173
  label="Upload PDF or image",
174
  file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp"],
175
  )
176
  run_btn = gr.Button("Run OCR", variant="primary")
177
+ out_md = gr.Textbox(label="Markdown", lines=30)
178
+ out_json = gr.Textbox(label="JSON (layout details)", lines=20)
179
+ run_btn.click(fn=run_ocr, inputs=file_in, outputs=[out_md, out_json])
180
  return demo
181
 
182
 
183
  if __name__ == "__main__":
184
+ build_demo().launch()