通过 open.bohrium.com 解析 PDF 文件。适用情形:用户询问从 Bohrium 上的 PDF 中提取文本、表格、图表、公式或分子时使用,提交...
Parse PDF documents using the open.bohrium.com PDF parsing service. Extract text, tables, charts, formulas, and molecular structures from PDFs. Two submission methods:
No CLI support — all operations use the HTTP API.
ACCESS_KEY is read from the OpenClaw config ~/.openclaw/openclaw.json:
json
"bohrium-pdf-parser": {
"enabled": true,
"apiKey": "YOUR_ACCESS_KEY",
"env": {
"ACCESS_KEY": "YOUR_ACCESS_KEY"
}
}
OpenClaw automatically injects env.ACCESS_KEY into the runtime.
python
import os, time, requests
AK = os.environ.get("ACCESS_KEY", "")
BASE = "https://open.bohrium.com/openapi/v1/parse"
HEADERS = {"accessKey": AK}
HEADERS_JSON = {**HEADERS, "Content-Type": "application/json"}
text
1. Submit PDF (URL or file upload) → get token
2. Poll result with token → complete when status == "success"
Synchronous mode ( sync=true) blocks until parsing completes but does not include content in the response — you still need get-result to retrieve it. Asynchronous mode ( sync=false, default) requires polling get-result until status is success.
python
r = requests.post(f"{BASE}/trigger-url-async", headers=HEADERS_JSON, json={
"url": "https://arxiv.org/pdf/2107.06922",
"sync": False,
"textual": True,
"table": True,
"molecule": True,
"chart": True,
"figure": False,
"expression": True,
"equation": True,
"pages": [0], # 0-indexed, omit to parse all pages
"timeout": 1800
})
data = r.json()
token = data["token"]
print(f"Token: {token}, Status: {data['status']}")
# Token: 57d12c5a-..., Status: undefined
Response Fields:
FieldDescriptiontokenTask identifier for querying resultsstatusInitial status is undefined``created_timeCreation timetime_dictPer-stage timing (only download_pdf at this point)
python
from pathlib import Path
pdf_path = Path("./paper.pdf")
with open(pdf_path, "rb") as f:
r = requests.post(f"{BASE}/trigger-file-async",
headers=HEADERS, # No Content-Type; requests handles multipart automatically
files={"file": (pdf_path.name, f, "application/pdf")},
data={
"sync": "false",
"textual": "true",
"table": "true",
"molecule": "true",
"chart": "true",
"figure": "false",
"expression": "true",
"equation": "true",
"pages": 0, # multipart only accepts a single integer
"timeout": 1800
})
token = r.json()["token"]
Important:
pagesin multipart/form-data only accepts a single integer (e.g.0), not a JSON array[0], or you'll get anint_parsingerror. In JSON request bodies, arrays like[0, 1, 2]are supported.
python
r = requests.post(f"{BASE}/get-result", headers=HEADERS_JSON, json={
"token": token,
"content": True, # Return extracted text
"objects": False, # Return extracted objects (tables, figures, etc.)
"pages_dict": True # Return per-page results
})
data = r.json()
print(f"Status: {data['status']}, Content length: {len(data.get('content', ''))}")
Response Fields:
FieldDescriptionstatus``success / undefined (processing) / failed``tokenTask identifiercontentExtracted text (LaTeX markup format)pages_dictPer-page result dictionarylangDetected language ( en / zh etc.)proc_page / total_pageProcessed / total pagesproc_textual / total_textualProcessed / total text blocksproc_table / total_tableProcessed / total tablesproc_mol / total_molProcessed / total moleculesproc_equa / total_equaProcessed / total equationstime_dictPer-stage timing detailscostCost
python
import os, time, requests
AK = os.environ.get("ACCESS_KEY", "")
BASE = "https://open.bohrium.com/openapi/v1/parse"
HEADERS = {"accessKey": AK}
HEADERS_JSON = {**HEADERS, "Content-Type": "application/json"}
# 1. Submit
r = requests.post(f"{BASE}/trigger-url-async", headers=HEADERS_JSON, json={
"url": "https://arxiv.org/pdf/2107.06922",
"sync": False,
"textual": True, "table": True, "molecule": False,
"chart": False, "figure": False,
"expression": True, "equation": True,
"pages": [0],
"timeout": 1800
})
submit = r.json()
if submit.get("code"):
print(f"Submit failed: {submit.get('message')}")
exit(1)
token = submit["token"]
print(f"Submitted, token={token}")
# 2. Poll for result
for attempt in range(30):
time.sleep(2)
r = requests.post(f"{BASE}/get-result", headers=HEADERS_JSON, json={
"token": token,
"content": True,
"objects": False,
"pages_dict": False
})
result = r.json()
status = result.get("status", "")
print(f" [{attempt+1}] status={status}")
if status == "success":
print(f"Done! Content length: {len(result.get('content', ''))}")
print(f"Language: {result.get('lang')}, Cost: {result.get('cost')}")
print(f"Preview: {result.get('content', '')[:200]}")
break
elif status == "failed":
print(f"Failed: {result.get('description', 'unknown error')}")
break
else:
print("Timeout: task did not complete within 60 seconds")
Synchronous mode ( sync=true) blocks until parsing completes, so no polling is needed. However, the response does not include the content field — you still need to call get-result to retrieve the parsed content:
python
# 1. Synchronous submit — blocks until parsing completes
r = requests.post(f"{BASE}/trigger-url-async", headers=HEADERS_JSON, json={
"url": "https://arxiv.org/pdf/2107.06922",
"sync": True, # Wait for completion
"textual": True, "table": True,
"molecule": False, "chart": False, "figure": False,
"expression": True, "equation": True,
"pages": [0],
"timeout": 1800
})
submit = r.json()
token = submit["token"]
# submit["status"] == "success", but no content field
# 2. Retrieve content
r = requests.post(f"{BASE}/get-result", headers=HEADERS_JSON, json={
"token": token,
"content": True, "objects": False, "pages_dict": False
})
result = r.json()
print(f"Content: {result['content'][:200]}")
ParameterTypeDefaultDescriptionsyncboolfalse``true blocks until complete (still need get-result for content), false requires pollingtextualbool-Extract text contenttablebool-Extract tablesmoleculebool-Extract molecular structureschartbool-Extract chartsfigurebool-Extract figures/imagesexpressionbool-Extract math expressionsequationbool-Extract equationspageslist[int]allPages to parse (0-indexed)timeoutint-Timeout in seconds
bash
AK="YOUR_ACCESS_KEY"
BASE="https://open.bohrium.com/openapi/v1/parse"
# URL submission
curl -s -X POST "$BASE/trigger-url-async" \
-H "Content-Type: application/json" \
-H "accessKey: $AK" \
-d '{"url":"https://arxiv.org/pdf/2107.06922","sync":false,"textual":true,"table":true,"molecule":false,"chart":false,"figure":false,"expression":true,"equation":true,"pages":[0],"timeout":1800}'
# File upload
curl -s -X POST "$BASE/trigger-file-async" \
-H "accessKey: $AK" \
-F "file=@paper.pdf" \
-F "sync=false" -F "textual=true" -F "table=true" \
-F "pages=0"
# Query result
curl -s -X POST "$BASE/get-result" \
-H "Content-Type: application/json" \
-H "accessKey: $AK" \
-d '{"token":"YOUR_TOKEN","content":true,"objects":false,"pages_dict":true}'
ProblemCauseSolutionAccessKey is requiredMissing or incorrect accessKeyHeader name is accessKey (case-sensitive), not Authorization: Bearer``int_parsing errorpages sent as JSON array in file uploadUse a single integer for pages in multipart formstatus: undefinedAsync task not yet completePoll get-result again; recommended interval: 2 secondsConnection timeoutDomain/network issueUse open.bohrium.com; test connectivity via curl -I https://open.bohrium.com/openapiContent has LaTeX markupNormal behaviorResults use \begin{title} etc. to mark structure; post-process to extract plain textLarge file parses slowlyMany pages or complex contentUse pages parameter to limit scope