Skip to content

feat: output.get_download_url() + SDK User-Agent identification#13

Merged
wei-hai merged 3 commits into
mainfrom
feat/output-get-download-url
Jul 23, 2026
Merged

feat: output.get_download_url() + SDK User-Agent identification#13
wei-hai merged 3 commits into
mainfrom
feat/output-get-download-url

Conversation

@wei-hai

@wei-hai wei-hai commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

What

  • Output.get_download_url() (sync + async) → DownloadUrl{ url, expires_at }: a directly-fetchable URL for an output, so a caller (e.g. a serverless worker) can hand the URL to a downstream consumer without streaming the bytes through itself.
    • Object-storage backends (Cloud/serverless): returns the self-authorizing signed URL (from the content endpoint's redirect Location) + its expiry.
    • Self-hosted (proxy serves inline): returns the content-endpoint URL, expires_at=None.
    • Works on every backend and never throws (write-once/run-anywhere). It's a short-lived, self-authorizing bearer credential — documented as such.
    • Per-output: a job with multiple outputs resolves each to its own distinct URL.
  • User-Agent identification on every request: comfy-sdk-python/{version} ({runtime}), with an optional client_info="my-app" appending an app/my-app token so an integration can attribute its own traffic. Request metadata only — not telemetry, no phone-home; a caller-supplied User-Agent still wins.

How

get_asset_content_url() issues GET /assets/{id}/content with a per-request follow_redirects=False (the shared client default is untouched), reads the redirect Location without following it (so the bearer can't leak cross-origin), and parses the GCS V4 X-Goog-Date/X-Goog-Expires params for the expiry.

Tests

pytest — 90 passed (incl. cloud-redirect, self-hosted-inline, multi-output-distinct-URLs, bearer-not-leaked, User-Agent format + client_info append). ruff + mypy clean. No OpenAPI/contract change (same GET /assets/{id}/content operation).

Add a first-class Output.get_download_url() returning {url, expires_at} — a
directly-fetchable URL for an output (signed URL on object-storage backends,
content URL on self-hosted; never throws). Also set a User-Agent identifying
the SDK on every request, with an optional client_info app token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@wei-hai
wei-hai requested review from a team as code owners July 23, 2026 06:45
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@wei-hai, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d76113a2-74b7-44ff-a490-519062698533

📥 Commits

Reviewing files that changed from the base of the PR and between efa87c4 and 0faed84.

📒 Files selected for processing (2)
  • src/comfy_low/transport.py
  • tests/test_get_download_url.py

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Too big: expected string to have <=250 characters at "tone_instructions"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

The SDK now sends identifiable User-Agent metadata with optional client information and exposes download URLs with optional signed-URL expiry timestamps for sync and async outputs.

Changes

SDK identity metadata

Layer / File(s) Summary
Client identity headers
src/comfy_low/transport.py, src/comfy_sdk/client.py, README.md, tests/conftest.py, tests/test_user_agent.py
Clients construct validated SDK User-Agent headers, forward client_info, document the behavior, and test default, custom, and rejected values.

Download URL resolution

Layer / File(s) Summary
Download URL resolution
src/comfy_low/transport.py, src/comfy_sdk/outputs.py, src/comfy_sdk/__init__.py, tests/conftest.py, tests/test_get_download_url.py
Sync and async transports resolve redirect or inline asset URLs, parse signed URL expiry, expose DownloadUrl, and test multi-output and redirect handling.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/output-get-download-url
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/output-get-download-url

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/comfy_low/transport.py (1)

125-126: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Honor caller User-Agent overrides case-insensitively.

{"user-agent": "custom"} leaves both User-Agent and user-agent in the mapping, so precedence becomes transport/server-dependent. Normalize the caller value by casefold before merging, and add a regression test for lowercase user-agent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/comfy_low/transport.py` around lines 125 - 126, Update the header merge
logic around the extra-header handling to detect and normalize caller-provided
User-Agent keys case-insensitively before merging, ensuring the caller’s value
is the sole effective User-Agent. Add a regression test covering lowercase
“user-agent” input and verify it overrides the transport default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/comfy_low/transport.py`:
- Around line 155-160: Update the expiry parsing flow around the datetime
calculation and X-Goog-Expires conversion to catch OverflowError from timedelta
or datetime addition, returning None for overflowing values just like other
malformed parameters. Preserve the existing successful return path for valid
expiry values.

---

Outside diff comments:
In `@src/comfy_low/transport.py`:
- Around line 125-126: Update the header merge logic around the extra-header
handling to detect and normalize caller-provided User-Agent keys
case-insensitively before merging, ensuring the caller’s value is the sole
effective User-Agent. Add a regression test covering lowercase “user-agent”
input and verify it overrides the transport default.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0e8294ad-99b0-41d8-a336-adbdb4c0a39c

📥 Commits

Reviewing files that changed from the base of the PR and between c02a255 and efa87c4.

📒 Files selected for processing (8)
  • README.md
  • src/comfy_low/transport.py
  • src/comfy_sdk/__init__.py
  • src/comfy_sdk/client.py
  • src/comfy_sdk/outputs.py
  • tests/conftest.py
  • tests/test_get_download_url.py
  • tests/test_user_agent.py

Comment thread src/comfy_low/transport.py Outdated
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@wei-hai
wei-hai merged commit 38cac1c into main Jul 23, 2026
10 checks passed
@wei-hai
wei-hai deleted the feat/output-get-download-url branch July 23, 2026 16:16
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.

1 participant