Skip to content

feat(cli): add offline PDF parsing CLI#4

Open
hzhaoy wants to merge 14 commits into
bytedance:mainfrom
hzhaoy:feat/cli
Open

feat(cli): add offline PDF parsing CLI#4
hzhaoy wants to merge 14 commits into
bytedance:mainfrom
hzhaoy:feat/cli

Conversation

@hzhaoy

@hzhaoy hzhaoy commented Jun 30, 2026

Copy link
Copy Markdown

Summary

  • add offline PDF parsing CLI under pdf_parser/cli
  • add CLI output/error handling helpers and page range support
  • add litepdf skill eval fixtures

Test

  • Not run in this PR creation step.

hzhaoy added 4 commits July 2, 2026 21:03
Add complete litepdf offline PDF to Markdown CLI tooling, including:
- CLI entrypoint with parse/batch commands and page range support
- Local parser using PyMuPDF for text/image extraction
- Error handling and JSON output envelopes
- Test fixtures and evaluation suites for validation
- Proper build configuration and dependency setup
This commit overhauls the CLI implementation:
1. Replaces argparse with Typer for better CLI UX and consistent error handling
2. Adds a proper custom PageSpecError type instead of using argparse ArgumentTypeError
3. Updates error code documentation and usage error messaging
4. Refactors the page range parsing validation to happen at the CLI layer
5. Maintains all original functionality while improving maintainability and error reporting
Unify the command surface around pdf-parser so parse and batch share the offline Markdown/envelope contract with serve in one Typer app. The old text/json parse path and litepdf console script are removed to avoid two meanings for parse, while the shared runner/output modules keep the parsing contract reusable. The bundled skill is renamed to pdf-parser so usage guidance matches the installed command.

Constraint: parse and batch must preserve the existing offline Markdown, manifest, profiling, JSON envelope, and NDJSON batch behavior
Rejected: Keep litepdf as a compatibility alias | user confirmed no scripts depend on it
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Do not reintroduce a second console script or parse contract without checking README, pyproject entry points, and skill trigger metadata
Tested: uv sync; uv run pdf-parser --help; uv run python -m pdf_parser --help; uv run pdf-parser parse skills/pdf-parser/evals/fixtures/normal.pdf --out /tmp/pdf-parser-skill-rename-smoke; uv run ruff check .; uv run ruff format --check .; uv run mypy pdf_parser; uv run --with pyyaml python /Users/bytedance/.codex/skills/.system/skill-creator/scripts/quick_validate.py skills/pdf-parser; git diff --check
Not-tested: Full HTTP server runtime beyond serve help output
This commit fully renames the project from pdf-parser to hi-pdf-parser:
- Updates all CLI command references, entry points, and package paths
- Moves all source code from pdf_parser/ to hi_pdf_parser/
- Updates pyproject.toml build configs and dependencies
- Updates pre-commit and CI pipeline paths
- Moves and updates skill manifests, eval fixtures and configs
- Removes old pdf-parser specific files and cleans up legacy code
- Fixes uv lockfile package source type
@hzhaoy hzhaoy changed the title feat(litepdf): add offline PDF parsing CLI feat(cli): add offline PDF parsing CLI Jul 2, 2026
Move logging setup out of the CLI namespace so command handlers can capture parser and app module logs through the shared hi_pdf_parser package logger. The server module no longer configures root logging at import time, leaving output routing to the entrypoint or host server.

Constraint: CLI stdout must remain reserved for JSON envelopes.

Rejected: Keep handlers on hi_pdf_parser.cli | parser module logs would still bypass per-document stderr.log.

Confidence: high

Scope-risk: narrow

Directive: Keep library modules emitting normal module loggers; configure handlers only from entrypoints.

Tested: uv run python -m unittest tests.test_logging_setup

Tested: uv run ruff check hi_pdf_parser tests

Tested: uv run ruff format --check hi_pdf_parser tests

Tested: uv run mypy hi_pdf_parser tests

Tested: hi-pdf-parser parse fixture preserves JSON stdout and captures parser logs in stderr.log

Tested: hi-pdf-parser serve health endpoint returned 200
Comment thread skills/hi-pdf-parser/evals/fixtures/fake.docx Outdated
Comment thread pyproject.toml
hzhaoy added 7 commits July 3, 2026 14:48
Centralize PyMuPDFParser creation behind a package-level factory so parse, batch, and serve do not maintain separate default config construction paths.

Constraint: Runtime options remain at each caller; HTTP request options and CLI page ranges are still passed to parser.parse per invocation.

Rejected: Move request/runtime options into the factory | that would make per-call behavior look like instance defaults and risk changing HTTP semantics.

Confidence: high

Scope-risk: narrow

Directive: Keep create_parser limited to parser instance defaults; do not route command output or HTTP response behavior through it.

Tested: uv run python -m unittest tests.test_logging_setup tests.test_parser_factory

Tested: uv run ruff check hi_pdf_parser tests

Tested: uv run ruff format --check hi_pdf_parser tests

Tested: uv run mypy hi_pdf_parser tests

Tested: hi-pdf-parser parse fixture returned success and wrote document.md

Tested: hi-pdf-parser serve health endpoint returned 200
Rename parse and batch support modules out of hi_pdf_parser.cli so the package has one command surface instead of a CLI-only support namespace. The moved modules now describe their command, artifact, envelope, and page-range responsibilities directly.

Constraint: Preserve parse, batch, and serve command behavior while removing the cli package boundary.

Rejected: Route serve through parse_runner | serve returns HTTP responses while parse_runner owns file artifacts, envelopes, and exit codes.

Confidence: high

Scope-risk: narrow

Directive: Keep output artifact/envelope code separate from HTTP response code; share only lower-level parser construction and parse options.

Tested: uv run python -m unittest tests.test_logging_setup tests.test_parser_factory

Tested: uv run ruff check hi_pdf_parser tests

Tested: uv run ruff format --check hi_pdf_parser tests

Tested: uv run mypy hi_pdf_parser tests

Tested: hi-pdf-parser parse fixture returned success and wrote document.md

Tested: hi-pdf-parser batch fixtures returned two success envelopes

Tested: hi-pdf-parser serve health endpoint returned 200
Remove the parse_runner module and keep parse/batch execution in the unified command entrypoint. The batch command now owns its aggregation loop directly, while parse and batch share a private single-file helper for the artifact/envelope workflow.

Constraint: Preserve stdout envelope contracts, per-document artifacts, exit codes, and serve behavior.

Rejected: Duplicate the full single-file parse flow inside both parse and batch | that would make error mapping and artifact writes drift.

Confidence: high

Scope-risk: moderate

Directive: Keep shared parse/batch file handling as private helpers unless another non-command surface needs the artifact/envelope contract.

Tested: uv run python -m unittest tests.test_commands tests.test_logging_setup tests.test_parser_factory

Tested: uv run ruff check hi_pdf_parser tests

Tested: uv run ruff format --check hi_pdf_parser tests

Tested: uv run mypy hi_pdf_parser tests

Tested: hi-pdf-parser parse fixture returned success and wrote document.md

Tested: hi-pdf-parser batch partial failure returned exit code 1 with success,error NDJSON

Tested: hi-pdf-parser serve health endpoint returned 200
Introduce ParseRuntimeOptions as the single per-call option object for parser.parse kwargs. CLI page ranges and HTTP request options now share the same conversion path while parser config defaults remain in parser_factory.

Constraint: Runtime options must stay per invocation; do not mix password, extract flags, or page ranges into parser instance defaults.

Rejected: Pass None-valued options through to parser.parse | omitting unspecified values preserves config fallback semantics and avoids noisy kwargs.

Confidence: high

Scope-risk: narrow

Directive: Add new per-call parser kwargs to ParseRuntimeOptions before wiring them through CLI or HTTP handlers.

Tested: uv run python -m unittest tests.test_app tests.test_commands tests.test_logging_setup tests.test_parser_factory tests.test_parse_runtime

Tested: uv run ruff check hi_pdf_parser tests

Tested: uv run ruff format --check hi_pdf_parser tests

Tested: uv run mypy hi_pdf_parser tests

Tested: hi-pdf-parser parse multipage fixture with --pages 1 reported one page

Tested: hi-pdf-parser batch partial failure returned exit code 1 with success,error NDJSON

Tested: hi-pdf-parser serve health endpoint returned 200
Inline the local success path into the single-file parse helper and rename the parser call helper to describe its returned blocks and metadata. This removes one command-layer hop without changing artifact, envelope, or error mapping behavior.

Constraint: Keep parser/PyMuPDF exception translation separate from artifact/envelope orchestration.

Rejected: Inline parser exception mapping into _parse_file | it would mix PyMuPDF details with command output handling.

Confidence: high

Scope-risk: narrow

Directive: Keep _parse_pdf_blocks focused on parser invocation and exception translation only.

Tested: uv run python -m unittest tests.test_app tests.test_commands tests.test_logging_setup tests.test_parser_factory tests.test_parse_runtime

Tested: uv run ruff check hi_pdf_parser tests

Tested: uv run ruff format --check hi_pdf_parser tests

Tested: uv run mypy hi_pdf_parser tests

Tested: hi-pdf-parser parse multipage fixture with --pages 1 reported one page

Tested: hi-pdf-parser batch partial failure returned exit code 1 with success,error NDJSON

Tested: hi-pdf-parser serve health endpoint returned 200
Keep the Typer entrypoint focused on command wiring while moving local parse execution into command_runner and artifact materialization into artifact_writer. Thin wrappers for page ranges, parser construction, and per-call parse options are removed because their behavior is now covered by direct call paths and tests.

Constraint: Do not reintroduce a cli support package or keep thin forwarding modules

Rejected: Keep ParseRuntimeOptions | it only wrapped direct kwargs after request parameters were reduced

Rejected: Keep parser_factory.py | construction helpers only forward into PyMuPDFParser

Confidence: high

Scope-risk: moderate

Directive: Keep parser-instance defaults in PyMuPDFParserConfig and pass per-call options directly

Tested: uv run python -m unittest discover -s tests

Tested: uv run ruff check --config=pyproject.toml hi_pdf_parser tests

Tested: uv run --no-sync mypy hi_pdf_parser

Tested: uv run ruff format --check --config=pyproject.toml hi_pdf_parser tests

Tested: git diff --check
Merge the latest origin/main release-prep changes into feat/cli while preserving the refactored parse and batch command boundaries. The conflict resolution keeps the command_runner path for offline parsing and adopts the optional server-extra import behavior for serve.

Constraint: origin/main moved FastAPI and uvicorn behind the server extra

Rejected: Restore the old direct parser CLI implementation | it would undo the command_runner boundary cleanup

Confidence: high

Scope-risk: moderate

Directive: Keep serve-only imports inside serve so CLI parse/batch work without server dependencies

Tested: uv lock --check

Tested: uv run --extra server python -m unittest discover -s tests

Tested: uv run ruff check --config=pyproject.toml hi_pdf_parser tests

Tested: uv run --extra server mypy hi_pdf_parser

Tested: uv run ruff format --check --config=pyproject.toml hi_pdf_parser tests

Tested: git diff --cached --check

Tested: uv run --no-sync python -c "import hi_pdf_parser.__main__; print('cli-import-ok')"
Comment thread hi_pdf_parser/__main__.py Outdated
Comment thread hi_pdf_parser/__main__.py Outdated
help="PDF Parser CLI and server.",
epilog="""示例:
hi-pdf-parser parse report.pdf --out ./out
hi-pdf-parser batch a.pdf b.pdf --out ./out

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不需要提供batch吧,比起分开执行并没有省计算

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

跟计算没关系,主要是在处理多个文件时 agent 只需要一次工具调用就可以

hzhaoy added 2 commits July 8, 2026 10:28
Separate the skill package artifacts from the CLI parser PR while keeping only the fixtures needed by command tests under tests/fixtures. Convert the CLI-facing help and error text to English so the command surface is consistent for reviewers and users.

Constraint: Review requested moving skill-related content to a separate PR

Constraint: Review requested English-only CLI text

Rejected: Remove batch | reviewer concern was discussed as tool-call ergonomics rather than compute savings

Confidence: high

Scope-risk: moderate

Directive: Keep skill packaging out of this PR; add it back in a separate skill/evals branch

Tested: uv run --extra server python -m unittest discover -s tests

Tested: uv run ruff check --config=pyproject.toml hi_pdf_parser tests

Tested: uv run --extra server mypy hi_pdf_parser

Tested: uv run ruff format --check --config=pyproject.toml hi_pdf_parser tests

Tested: git diff --cached --check

Tested: rg -n '[\p{Han}]' hi_pdf_parser tests README.md pyproject.toml

Tested: rg -n 'skills/|hi-pdf-parser/evals|evals/fixtures' .

Tested: uv run --no-sync hi-pdf-parser --help

Tested: uv run --no-sync hi-pdf-parser parse tests/fixtures/multipage.pdf --pages 1,3
Consolidate multi-file handling under parse, remove obsolete mode metadata, and give the output helpers concise responsibility-based module names.

Constraint: Keep the CLI surface limited to parse and serve without a cli package.

Rejected: Preserve batch and legacy options as aliases | the approved interface intentionally removes them.

Rejected: Add absence assertions for removed mode fields | explicitly excluded from the requested test scope.

Confidence: high

Scope-risk: moderate

Directive: Keep parser.py independent of CLI artifact and envelope concerns.

Tested: unittest discover (15 tests); Ruff check; Ruff format check; mypy; CLI help smoke; git diff check

Not-tested: Downstream consumers of the removed batch command and envelope fields
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants