Skip to content

Support http(s) URLs in vlmrun gw chat - #206

Merged
spillai merged 2 commits into
mainfrom
cursor/gw-chat-url-support-1c7e
Aug 5, 2026
Merged

Support http(s) URLs in vlmrun gw chat#206
spillai merged 2 commits into
mainfrom
cursor/gw-chat-url-support-1c7e

Conversation

@spillai

@spillai spillai commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

vlmrun gw chat now accepts http(s) URLs inline alongside local file paths. URLs are sent to the gateway as remote content parts (image_url, document_url, or video_url) based on the URL path extension, rather than being base64-encoded.

Bumps version to 0.7.2.

Usage

vlmrun gw chat https://example.com/scan.jpg -m paddleocr/pp-ocrv6
vlmrun gw chat https://example.com/report.pdf -m zai-org/glm-ocr
vlmrun gw chat local.png https://example.com/doc.pdf -m zai-org/glm-ocr

Changes

  • Chat arguments changed from List[Path] (with exists=True) to List[str] so URLs can be passed inline
  • Added URL detection and content-part encoding helpers (_encode_url_part, _content_part_type_from_url, etc.)
  • Document URLs still enable streaming; image/video URLs do not
  • Local video files (.mp4, etc.) now use video_url content parts for consistency with URL inputs
  • Version bumped to 0.7.2

Testing

  • Added unit tests for URL encoding, mixed file/URL inputs, CLI validation, and streaming behavior
  • Verified against live gateway with VLMRUN_API_KEY=vlmrun:
    • PDF URL → zai-org/glm-ocr succeeded
    • Image URL → paddleocr/pp-ocrv6 succeeded
Open in Web Open in Cursor 

Open in Devin Review

Allow inline URL arguments for images, documents, and videos alongside
local file paths. URLs are passed directly to the gateway as remote
content parts (image_url, document_url, video_url) instead of being
base64-encoded.

Co-authored-by: Sudeep Pillai <sudeep.pillai@gmail.com>
Co-authored-by: Sudeep Pillai <sudeep.pillai@gmail.com>

@devin-ai-integration devin-ai-integration Bot left a comment

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.

Devin Review found 2 potential issues.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +222 to +232
def _validate_chat_input(raw: str) -> None:
"""Ensure a non-URL chat input refers to a readable local file."""
if _is_http_url(raw):
return
path = Path(raw).expanduser()
if not path.is_file():
console.print(
f"[red]Error:[/] Input '{raw}' is not a file. "
"Provide a local path or an http(s) URL."
)
raise typer.Exit(1)

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.

🟡 Unreadable input files now crash with a raw traceback instead of a clear error

Chat inputs are only checked for existence (path.is_file() at vlmrun/cli/_cli/gateway.py:227) and no longer for read permission, so pointing the command at a file you cannot read ends in an unhandled crash instead of a friendly message.
Impact: Users see a Python stack trace rather than a clear "cannot read file" error.

Loss of typer's readable=True validation

The argument previously declared exists=True, readable=True on a List[Path], so typer rejected unreadable paths with a clean usage error. Now _validate_chat_input (vlmrun/cli/_cli/gateway.py:222-232) only checks is_file(); the subsequent path.stat() in the tree rendering (vlmrun/cli/_cli/gateway.py:694) and path.read_bytes() in _encode_file_part (vlmrun/cli/_cli/gateway.py:195) will raise PermissionError/OSError uncaught.

Suggested change
def _validate_chat_input(raw: str) -> None:
"""Ensure a non-URL chat input refers to a readable local file."""
if _is_http_url(raw):
return
path = Path(raw).expanduser()
if not path.is_file():
console.print(
f"[red]Error:[/] Input '{raw}' is not a file. "
"Provide a local path or an http(s) URL."
)
raise typer.Exit(1)
def _validate_chat_input(raw: str) -> None:
"""Ensure a non-URL chat input refers to a readable local file."""
if _is_http_url(raw):
return
path = Path(raw).expanduser()
if not path.is_file():
console.print(
f"[red]Error:[/] Input '{raw}' is not a file. "
"Provide a local path or an http(s) URL."
)
raise typer.Exit(1)
if not os.access(path, os.R_OK):
console.print(f"[red]Error:[/] Input '{raw}' is not readable.")
raise typer.Exit(1)
Open in Devin Review

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

return Path(urlparse(url).path).suffix.lower()


def _content_part_type_for_suffix(suffix: str, mime: Optional[str] = None) -> str:

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 code uses outdated type-hint style the repository guidelines forbid

The newly added helpers and the changed command signature use legacy typing constructs (Optional[str] at vlmrun/cli/_cli/gateway.py:156) instead of the modern syntax the repository's contributor guide mandates.
Impact: The change conflicts with the project's documented code-style requirements.

AGENTS.md modern Python style rule

AGENTS.md requires X | None over Optional[X] and built-in generics (list[T], dict[K, V]) over List[T]/Dict[K, V]. New/changed code violating this: _content_part_type_for_suffix signature (vlmrun/cli/_cli/gateway.py:156), _encode_url_part/_encode_chat_input returning Dict[str, Any] (vlmrun/cli/_cli/gateway.py:205-219), _build_messages(inputs: List[str], prompt: Optional[str]) -> List[Dict[str, Any]] (vlmrun/cli/_cli/gateway.py:270), and inputs: List[str] in chat (vlmrun/cli/_cli/gateway.py:597). Note the surrounding file already uses the legacy style, so a broader cleanup may be preferred.

Suggested change
def _content_part_type_for_suffix(suffix: str, mime: Optional[str] = None) -> str:
def _content_part_type_for_suffix(suffix: str, mime: str | None = None) -> str:
Open in Devin Review

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

@spillai
spillai merged commit 6ac3d4f into main Aug 5, 2026
4 checks passed
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.

3 participants