Documents¶
Document channels (folder tree), per-channel pipelines and metadata extraction, and the PaddleOCR-VL parsing pipeline driven by openkms-cli.
Document channels and CRUD¶
| Feature | Status | Description |
|---|---|---|
| Document overview | ✅ | Dashboard at /documents with channel count, document count (from API stats), quick actions |
| Channel management | ✅ | Create channels at /documents/channels (tree structure); rename, description, move, merge, delete; settings per channel |
| Document channel view | ✅ | Browse documents by channel at /documents/channels/:channelId; list from GET /api/documents?channel_id= with pagination (offset/limit, page size 10–100), status filter, applicable filter (lifecycle-derived; same rules as is_current_for_rag), and toolbar refresh; multi-select on the current page with bulk Process, Reset status (to uploaded for re-processing), Move, and Delete |
| Channel settings | ✅ | Per-channel pipeline, auto-process, metadata extraction (model + schema, supports object_type/list[object_type]), manual labels config at /documents/channels/:channelId/settings; tabbed UI (General, Processing, Metadata extraction, Manual Labels) |
| Document upload | ✅ | Upload to channel via modal (choose files, drag-and-drop); POST /api/documents/upload with channel_id; stores file to S3 under documents/{file_hash}/ (reads fall back to legacy {file_hash}/ until migrated via Console object storage). Accepted types: PDF, PNG/JPG/JPEG/WEBP, DOCX, PPTX, XLSX, EPUB, XMIND. XLSX: preview rows + markdown built at upload (status=completed or failed if unreadable); no VLM pipeline. XMIND: outline markdown from content.json / content.xml at upload (notes, labels, markers, hyperlinks; attachment paths listed); no VLM pipeline. DOCX/PPTX/EPUB (and other pipeline types): status=uploaded until processed |
| Document processing | ✅ | Process button on list/detail → POST /api/jobs (optional force_reparse; Job runs UI checkbox). Disabled with tooltip when the channel has no default pipeline (PDF/images/Office); XLSX / XMIND do not require a pipeline. XLSX runs use run_spreadsheet_preview; XMIND runs use run_mindmap_preview. Other types: force_reparse false reuses {file_hash}/result.json on storage when present; if the channel has metadata extraction and document metadata schema fields are all empty (checked via DB / GET /internal-api/documents/{id}/metadata-needs-extraction), the worker still runs openkms-cli to extract metadata only (parse skipped in CLI for paddleocr-doc-parse and baidu-doc-parse). force_reparse true or Retry on a failed run always runs the full VLM CLI again. Channel auto-process uses force_reparse=false |
| Document status | ✅ | Status badge (uploaded/pending/running/completed/failed) on document list and detail |
| Document detail | ✅ | View parsed Markdown at /documents/view/:id; layout skeleton while loading; Document Information: 3-column stats (Type, Size, Uploaded | Status, Markdown, File hash | Version panel with Versions + conditional Commit version when working copy changed since last snapshot); METADATA section includes Lineage & lifecycle below Extract (collapsed by default; expands for series, relationships, lifecycle, dates, and read-only Applicable); right panel: Markdown | Page Index (refresh parses markdown to tree; Page Index hidden for XLSX); Markdown read mode includes a Print action that opens Chrome/system print with a print-only single-column markdown layout; large books / long documents can defer the page-image pane until requested so the page stays responsive; XLSX: left panel Workbook with sheet tabs and scrollable grid from parsing_result; explicit versions (document_versions) not created on routine save; scrollable layout (min-height 720px) |
| Document markdown edit | ✅ | Edit/View toggle, textarea for markdown, Save (PUT /markdown; rebuilds page index), Restore from S3 (POST /restore-markdown; rebuilds page index); POST /rebuild-page-index for manual rebuild from current markdown; Save in the panel header uses the same outlined control style as Edit/Cancel with accent emphasis (icon + label); print mode is read-only and hides app chrome / image pane for cleaner browser PDF output |
| Document versions | ✅ | User-triggered checkpoints: POST /documents/{id}/versions snapshots current markdown and metadata (optional tag in API); list, preview, restore (POST .../versions/{vid}/restore); optional commit-current before restore; Commit version modal (optional tag) |
| Document metadata extraction | ✅ | Single METADATA section on detail page; Extract button uses channel's LLM; configurable schema per channel (key, label, type: text/date/enum/object_type/list[object_type], description); object_type_extraction_max_instances limits instance count for extraction |
| Document info & metadata edit | ✅ | Edit document name and channel (PUT /api/documents/{id}); Edit metadata fields inline (PUT /metadata); Move document to channel via modal |
| Document metadata (unified) | ✅ | All metadata (extracted + manual) in single metadata JSONB; manual labels configure in channel settings Manual Labels tab (type: object_type or list[object_type]); object-instance pickers in METADATA section |
| Channel description | ✅ | Channel description shown on channel page; stored in document_channels.description |
| Parsing export/import | ✅ | Icon buttons on document detail NAME row: download all parsing files as zip, upload a previously exported zip (auto chunked > 10 MB). Restores S3 files, markdown, parsing_result, status→completed, rebuilds page index. For backup and transferring parse results between deployments without re-processing |
Document parsing (PaddleOCR-VL and Baidu Cloud)¶
- PaddleOCR-VL with mlx-vlm-server as VLM backend (
paddleocr-doc-parsepipeline / defaultparse run) - Baidu Cloud PaddleOCR-VL API (
baidu-doc-parsepipeline /parse run --method baidu-doc-parse) — uploads to private Baidu BOS, submits presignedfile_urlto paddle-vl-parser; credentialsOPENKMS_BAIDU_CLOUD_*, bucketOPENKMS_BAIDU_BOS_BUCKET, optionalBAIDU_*_URLinopenkms-cli/.env; disable BOS hotlink protection; page preview PNGs and bbox coords use 2×page_meta(same grid as local VLM) - Supports: PDF, PNG, JPG, JPEG, WEBP; DOCX and PPTX are converted to PDF with LibreOffice (
soffice/libreoffice) in the worker/CLI; EPUB is converted to PDF with MuPDF (mutool; packagemupdf-toolsin the worker image) using stream compression so large technical EPUBs stay under Baidu's 100MB PDF limit, then parsed like PDF - Output: Markdown, layout detection, parsing result JSON
- Configurable: server URL, model, max concurrency
- Channel metadata extraction during pipeline: if the extraction LLM errors (e.g. HTTP 502), the CLI logs a warning and still completes the parse so the document can reach
completed; use Extract on the document page when the model is healthy. Extracted fields are written to{file_hash}/extracted_metadata.jsonon storage; the worker merges them into the document row when the job finishes. Before LLM extraction, openkms-cli callsGET /internal-api/documents/{id}/metadata-needs-extraction; skips extraction when channel schema fields already have values. On re-process with cached parse output, metadata extraction runs only when that check reports empty fields (both paddleocr-doc-parse and baidu-doc-parse).
openkms-cli¶
- CLI at
openkms-cli/built with Typer (≥0.9.0) - Tests:
openkms-cli/tests/—pip install -e ".[dev]" && pytest tests/(VLM defaults merge / fetch wiring with mocks; parser restructure and bbox/layout helpers; no Paddle install required) - Configuration:
openkms_cli/settings.py(CliSettings, pydantic-settings) lists every supported env var viavalidation_alias; parse/pipeline/auth read throughget_cli_settings(); Typer no longer duplicates env viaenvvar= - Parse:
openkms-cli parse run <input> [--output dir] [--method paddleocr-doc-parse|baidu-doc-parse] [--vlm-url ...]; inputs: PDF, images, DOCX, PPTX (LibreOffice), EPUB (mutool); VLM URL/model/key can followGET /internal-api/models/document-parse-defaultswhenOPENKMS_API_URLis set, neededOPENKMS_VLM_*values are missing, and CLI auth succeeds; whenOPENKMS_VLM_MODELis set in the environment, the CLI sends?model_name=...so the backend returns thatvl/ocrrow's URL and key, or the default row if there is no match - Pipeline:
openkms-cli pipeline list(list supported pipelines);openkms-cli pipeline run --pipeline-name paddleocr-doc-parse|baidu-doc-parse --input s3://.../original.<ext>– S3 or local input (stored key preserves extension); optional --s3-prefix (defaults to file hash), --skip-upload - Metadata extraction: when channel has extraction_model_id and extraction_schema, worker passes
--extract-metadata --extraction-model-name <model_name>; CLI fetches model config fromGET /internal-api/models/config-by-name, extracts via pydantic-ai PromptedOutput(StructuredDict) withresponse_format: json_object(DeepSeek-compatible), PUTs toPUT /internal-api/documents/{id}/metadata(system path; no channel write ACL); LLM failure does not fail the pipeline after a successful parse - Uses PaddleOCR-VL for parsing (optional:
pip install openkms-cli[parse]); pipeline needspip install openkms-cli[pipeline]; extraction needspip install openkms-cli[metadata]; PageIndex tree built-in (md_to_tree uses # headings) - Output structure on storage:
documents/{file_hash}/original.{ext},result.json,markdown.md,page_index.json(when pageindex installed),layout_det_*,block_*,markdown_out/*(logical paths inresult.jsonmay still use{file_hash}/…; API resolves to the new prefix) - Backend integration: subprocess-invokable for async jobs
- Extensible: developers can add new Typer subapps in app.py