AILANG Parse Python SDK
Python client and MCP server for the AILANG Parse document parsing API. Parse 16 formats (including LaTeX/arXiv and RTF), generate 8 — zero dependencies for Office, pluggable AI for PDFs.
Install
pip install ailang-parse
MCP Server (Claude Desktop, Cursor, VS Code)
Run as a stdio MCP server that bridges to the hosted AILANG Parse API. Stdlib only — works in any Python >= 3.8 environment.
{
"mcpServers": {
"ailang-parse": {
"command": "uvx",
"args": ["ailang-parse", "mcp"]
}
}
}
Add to claude_desktop_config.json (Claude Desktop), .cursor/mcp.json (Cursor), or .vscode/settings.json (VS Code). Provides 7 tools: parse, convert, formats, estimate, auth, auth-poll, and account.
Quick Start
from ailang_parse import DocParse
client = DocParse(api_key="dp_your_key_here")
result = client.parse("report.docx")
print(f"{len(result.blocks)} blocks, format: {result.format}")
for block in result.blocks:
if block.type == "heading":
print(f" H{block.level}: {block.text}")
elif block.type == "table":
print(f" Table: {len(block.headers)} cols, {len(block.rows)} rows")
elif block.type == "change":
print(f" {block.change_type} by {block.author}: {block.text}")
else:
print(f" {block.type}: {block.text[:80]}")
Parse Documents
result = client.parse("report.docx")
result = client.parse("report.docx", output_format="markdown")
result = client.parse("report.docx", output_format="html")
result = client.parse("report.docx", output_format="markdown+metadata")
result = client.parse_file("local/report.docx")
result = client.parse_url(
"https://storage.googleapis.com/bucket/doc.docx?X-Goog-Signature=...",
output_format="markdown+metadata",
)
print(result.status)
print(result.filename)
print(result.format)
print(result.blocks)
print(result.metadata.title)
print(result.metadata.author)
print(result.summary.tables)
print(result.markdown)
for section in result.sections:
print(f" {section.heading}: {section.markdown[:60]}...")
Response Metadata
Every parse result includes quota and request metadata from response headers:
result = client.parse("report.docx")
meta = result.response_meta
print(meta.request_id)
print(meta.tier)
print(meta.quota_remaining_day)
print(meta.quota_remaining_month)
print(meta.quota_remaining_ai)
print(meta.format)
print(meta.replayable)
Error Handling
Every error type carries the response headers — request_id for log
correlation, replayable for retry decisions, plus details and
suggested_fix from the response body:
from ailang_parse import DocParse, DocParseError, AuthError, QuotaError
client = DocParse()
try:
result = client.parse_file("report.docx")
except AuthError as e:
log.error("auth: %s request_id=%s", e, e.request_id)
except QuotaError as e:
log.error("quota tier=%s request_id=%s", e.tier, e.request_id)
except DocParseError as e:
log.error("error: %s status=%d replayable=%s request_id=%s",
e, e.status_code, e.replayable, e.request_id)
Retries
Opt in to retries with RetryPolicy. respect_replayable honours the
server-provided X-AilangParse-Replayable header so 5xx responses the
server explicitly marks safe-to-retry are attempted again:
from ailang_parse import DocParse, RetryPolicy
client = DocParse(retry=RetryPolicy(
max_retries=3,
retryable_statuses={502, 503, 504},
respect_replayable=True,
))
Parse from GCS
The parse_gs_uri convenience signs a gs:// URI and parses it in one
call. Requires the gcs extra:
pip install 'ailang-parse[gcs]'
result = client.parse_gs_uri(
"gs://my-bucket/path/to/doc.pdf",
ttl=900,
output_format="markdown+metadata",
)
Auth defaults to Application Default Credentials; pass an explicit
credentials= (or service_account_email=) to override.
Signing strategy (auto-detected)
- JSON-key credentials (
GOOGLE_APPLICATION_CREDENTIALS pointing at
an SA key file) → URL is signed locally with the private key. Fast,
no extra API call.
- Token-only credentials (Cloud Run, GCE, GKE metadata server, or
gcloud auth application-default login) → URL is signed via Google's
IAM SignBlob API. No private key needed.
Cloud Run setup (one-time): the runtime service account needs
roles/iam.serviceAccountTokenCreator on itself:
SA="my-service@my-project.iam.gserviceaccount.com"
gcloud iam service-accounts add-iam-policy-binding "$SA" \
--member="serviceAccount:$SA" \
--role="roles/iam.serviceAccountTokenCreator"
After the grant, client.parse_gs_uri("gs://...") works from any
Cloud Run service running as that SA with no further config.
For impersonation flows, pass an explicit
service_account_email="signer@project.iam.gserviceaccount.com".
RAG Chunking
result.flatten(policy) turns the Block ADT into JSON-friendly chunks
ready for an embedder. The default policy emits text, headings, table
rows (with header context), and lists — and tracks section ancestry:
from ailang_parse import FlattenPolicy
chunks = result.flatten(FlattenPolicy(
max_chunk_chars=4000,
embed_images=True,
embed_changes=True,
embed_comments=True,
on_table="row",
on_table_cell_newlines="space",
on_table_cell_pipes="escape",
section_path=True,
))
for c in chunks:
embed(c.text, metadata=c.metadata.to_dict())
Custom chunk metadata
Use metadata.extras to carry consumer-defined fields. The on_table
callable receives a mutable ChunkMetadata and can populate it:
def my_table(block, md):
md.extras["tenant_id"] = "acme"
md.extras["confidence"] = 0.93
return [Chunk(text=..., metadata=md)]
chunks = result.flatten(FlattenPolicy(on_table=my_table))
extras values should be JSON-serializable — they pass through to
Pinecone/Vertex/Chroma metadata unchanged.
Image visibility
embed_images=True always emits an ImageBlock chunk. When the image
has no AI caption, the chunk text is a placeholder
("[image: image/png, 12345 bytes]") and
metadata.extras["image_has_description"] is False. To match the
v0.6.0 "skip empty" behaviour:
chunks = [
c for c in result.flatten(FlattenPolicy(embed_images=True))
if c.metadata.block_type != "image"
or c.metadata.extras.get("image_has_description")
]
Supported Formats
formats = client.formats()
print(formats.parse)
print(formats.generate)
print(formats.ai_required)
Block Types
AILANG Parse returns 9 block types:
text | text, style, level | Paragraphs, code blocks |
heading | text, level (1-6) | Document headings |
table | headers, rows | Tables with merge tracking |
list | items, ordered | Ordered/unordered lists |
image | description, mime, data_length | Embedded images |
audio | transcription, mime | Audio transcriptions |
video | description, mime | Video descriptions |
section | kind, children | Slides, sheets, headers/footers |
change | change_type, author, date, text | Track changes |
Table cells
Table cells can be simple strings or merged cells:
for block in result.blocks:
if block.type == "table":
for cell in block.headers:
print(f" {cell.text} (colspan={cell.col_span}, merged={cell.merged})")
Nested sections
Section blocks contain child blocks (slides, sheets, headers/footers):
for block in result.blocks:
if block.type == "section":
print(f"Section: {block.kind}")
for child in block.children:
print(f" {child.type}: {child.text[:50]}")
API Key Management
API key resolution (checked in order):
- Explicit
api_key parameter
DOCPARSE_API_KEY environment variable
- Saved credentials in
~/.config/ailang-parse/credentials.json
Use the device auth flow to get an API key. The user signs in once — the key is saved automatically and reused in future sessions.
from ailang_parse import DocParse
client = DocParse()
client.device_auth(label="my-agent")
client = DocParse()
result = client.parse("report.docx")
client = DocParse()
result = client.parse("report.docx")
usage = client.keys.usage(key_id="abc123", user_id="user123")
print(f"Requests today: {usage.usage.requests_today} / {usage.quota.requests_per_day}")
new_key = client.keys.rotate(key_id="abc123", user_id="user123")
print(new_key.key)
client.keys.revoke(key_id="abc123", user_id="user123")
Migrating from Unstructured
One import change:
from unstructured_client import UnstructuredClient
client = UnstructuredClient(server_url="https://api.unstructured.io")
from ailang_parse import UnstructuredClient
client = UnstructuredClient(
server_url="https://api.parse.sunholo.com"
)
elements = client.general.partition(file="report.docx")
for el in elements:
print(f"{el.type}: {el.text[:80]}")
print(f" metadata: {el.metadata.filename}")
Error Handling
from ailang_parse import DocParse, DocParseError, AuthError, QuotaError
client = DocParse(api_key="dp_invalid")
try:
result = client.parse("file.docx")
except AuthError as e:
print(f"Bad key: {e}")
except QuotaError as e:
print(f"Quota exceeded: {e}")
except DocParseError as e:
print(f"API error ({e.status_code}): {e}")
print(f" suggested fix: {e.suggested_fix}")
print(f" details: {e.details}")
print(f" request_id: {e.request_id}")
Configuration
client = DocParse(
api_key="dp_your_key",
base_url="https://your-deployment.run.app",
timeout=120,
)
License
Apache 2.0 — see LICENSE for details.
Links