Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions tests/test_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,84 @@ def test_chat_extra_routes_gateway_field_to_extra_body(
assert call["extra_body"] == {"document_dpi": 200}
assert call["temperature"] == 0

def test_chat_document_dpi_rides_in_extra_body(self, runner, patched_cli, tmp_path):
pdf = tmp_path / "doc.pdf"
pdf.write_bytes(b"%PDF fake")
result = runner.invoke(
app,
["gw", "chat", str(pdf), "-m", "pp-ocrv6", "--document-dpi", "150", "-ns"],
)
assert result.exit_code == 0, result.stdout
call = patched_cli["client"].gateway.completions.calls[-1]
assert call["extra_body"] == {"document_dpi": 150}
# A gateway field must not leak into create()'s own kwargs.
assert "document_dpi" not in call

def test_chat_document_dpi_short_flag(self, runner, patched_cli, tmp_path):
pdf = tmp_path / "doc.pdf"
pdf.write_bytes(b"%PDF fake")
result = runner.invoke(
app, ["gw", "chat", str(pdf), "-m", "pp-ocrv6", "-d", "300", "-ns"]
)
assert result.exit_code == 0, result.stdout
assert patched_cli["client"].gateway.completions.calls[-1]["extra_body"] == {
"document_dpi": 300
}

def test_chat_document_max_pages_rides_in_extra_body(
self, runner, patched_cli, tmp_path
):
pdf = tmp_path / "doc.pdf"
pdf.write_bytes(b"%PDF fake")
result = runner.invoke(
app,
[
"gw",
"chat",
str(pdf),
"-m",
"pp-ocrv6",
"--document-max-pages",
"10",
"-ns",
],
)
assert result.exit_code == 0, result.stdout
assert patched_cli["client"].gateway.completions.calls[-1]["extra_body"] == {
"document_max_pages": 10
}

def test_chat_dpi_merges_with_method_and_extra(self, runner, patched_cli, tmp_path):
pdf = tmp_path / "doc.pdf"
pdf.write_bytes(b"%PDF fake")
result = runner.invoke(
app,
[
"gw",
"chat",
str(pdf),
"-m",
"pp-ocrv6",
"--method",
"ocr",
"--document-dpi",
"150",
"--document-max-pages",
"50",
"-e",
"temperature=0",
"-ns",
],
)
assert result.exit_code == 0, result.stdout
call = patched_cli["client"].gateway.completions.calls[-1]
assert call["extra_body"] == {
"method": "ocr",
"document_dpi": 150,
"document_max_pages": 50,
}
assert call["temperature"] == 0

def test_chat_no_extra_body_when_unused(self, runner, patched_cli, tmp_path):
f = tmp_path / "img.png"
f.write_bytes(b"fakepng")
Expand Down
30 changes: 30 additions & 0 deletions vlmrun/cli/_cli/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
such as ``zai-org/glm-ocr`` and ``paddleocr/pp-ocrv6``) do not accept
text-only input.

Multi-page PDFs are sent whole; the gateway rasterizes and fans them out per
page. ``--document-dpi`` tunes that rasterization (default 72; raise to
150-300 for dense or high-resolution pages) and ``--document-max-pages`` caps
how many pages are processed.

Commands: ``health``, ``models`` (list or detail one model), ``chat``,
``embed`` (embeddings) and ``transcribe`` (audio transcriptions).
"""
Expand Down Expand Up @@ -55,6 +60,7 @@
vlmrun gw chat a.pdf b.pdf -m paddleocr/pp-ocrv6
vlmrun gw chat img.jpg -m paddleocr/pp-ocrv6
vlmrun gw chat img.jpg -p "describe this image" -m qwen/qwen3.5-0.8b
vlmrun gw chat doc.pdf -m paddleocr/pp-ocrv6 --document-dpi 150
vlmrun gw chat doc.pdf -m zai-org/glm-ocr -e temperature=0 -e max_tokens=4096

\b
Expand All @@ -71,6 +77,8 @@
aliases (e.g. `glm-ocr`) also work.
Most gateway models (e.g. OCR models) require at least one input file and do
not accept text-only prompts. Use -p only for models that support it.
PDFs are sent whole; the gateway rasterizes and fans them out per page. Raise
`--document-dpi` (default 72) to 150-300 if small or dense text is missed.
"""

GATEWAY_HELP = """OCR, VLM, embedding and transcription models on the VLM Run gateway.
Expand Down Expand Up @@ -562,6 +570,21 @@ def chat(
"--method-params",
help='JSON object of method arguments, e.g. \'{"lang": "en"}\'.',
),
document_dpi: Optional[int] = typer.Option(
None,
"--document-dpi",
"-d",
help=(
"Rasterization DPI per PDF page (gateway default 72; 150 is a good "
"balance; 300+ preserves fine print on dense/high-res pages at "
"higher cost). PDF input only."
),
),
document_max_pages: Optional[int] = typer.Option(
None,
"--document-max-pages",
help="Cap the number of PDF pages processed (gateway default 500).",
Comment on lines +573 to +586

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 New command options use outdated type-hint style disallowed by repo guidelines

The two new options are annotated with Optional[int] (vlmrun/cli/_cli/gateway.py:573 and vlmrun/cli/_cli/gateway.py:583), but the repository's mandatory style guide requires the modern int | None union syntax for all new code.
Impact: The new code does not follow the repository's required modern Python type-hint style.

Rule reference in AGENTS.md

AGENTS.md, section "Modern Python Style", states: "Use X | None instead of Optional[X] for type hints (PEP 604)." The newly added parameters document_dpi and document_max_pages use Optional[int] instead of int | None. Note the surrounding file already uses Optional throughout, so this is a pre-existing convention in the module, but the rule explicitly applies to new code.

Suggested change
document_dpi: Optional[int] = typer.Option(
None,
"--document-dpi",
"-d",
help=(
"Rasterization DPI per PDF page (gateway default 72; 150 is a good "
"balance; 300+ preserves fine print on dense/high-res pages at "
"higher cost). PDF input only."
),
),
document_max_pages: Optional[int] = typer.Option(
None,
"--document-max-pages",
help="Cap the number of PDF pages processed (gateway default 500).",
document_dpi: int | None = typer.Option(
None,
"--document-dpi",
"-d",
help=(
"Rasterization DPI per PDF page (gateway default 72; 150 is a good "
"balance; 300+ preserves fine print on dense/high-res pages at "
"higher cost). PDF input only."
),
),
document_max_pages: int | None = typer.Option(
None,
"--document-max-pages",
help="Cap the number of PDF pages processed (gateway default 500).",
),
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

),
response_format: Optional[str] = typer.Option(
None,
"--response-format",
Expand Down Expand Up @@ -614,6 +637,13 @@ def chat(
raise typer.Exit(1)
extra_body["method_params"] = parsed_params

# Document rasterization controls are gateway-specific, so they ride in
# extra_body as top-level request-body fields (like `method`).
if document_dpi is not None:
extra_body["document_dpi"] = document_dpi
if document_max_pages is not None:
extra_body["document_max_pages"] = document_max_pages

if response_format:
# A standard OpenAI create() field, so it rides as a top-level kwarg.
create_kwargs["response_format"] = _parse_response_format(response_format)
Expand Down
Loading