[PR #13] [MERGED] feat: output.get_download_url() + SDK User-Agent identification #21

Closed
opened 2026-10-26 22:40:44 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Comfy-Org/comfy-python-sdk/pull/13
Author: @wei-hai
Created: 7/23/2026
Status: ✅ Merged
Merged: 7/23/2026
Merged by: @wei-hai

Base: main ← Head: feat/output-get-download-url


📝 Commits (3)

  • 997acd9 feat: output.get_download_url() + SDK User-Agent identification
  • efa87c4 harden: reject CR/LF in client_info to prevent header injection
  • 0faed84 fix: treat overflowing X-Goog-Expires as no-expiry (return None)

📊 Changes

8 files changed (+404 additions, -21 deletions)

View changed files

📝 README.md (+9 -0)
📝 src/comfy_low/transport.py (+117 -5)
📝 src/comfy_sdk/__init__.py (+2 -1)
📝 src/comfy_sdk/client.py (+4 -2)
📝 src/comfy_sdk/outputs.py (+36 -1)
📝 tests/conftest.py (+25 -12)
➕ tests/test_get_download_url.py (+170 -0)
➕ tests/test_user_agent.py (+41 -0)

📄 Description

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).


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Comfy-Org/comfy-python-sdk/pull/13 **Author:** [@wei-hai](https://github.com/wei-hai) **Created:** 7/23/2026 **Status:** ✅ Merged **Merged:** 7/23/2026 **Merged by:** [@wei-hai](https://github.com/wei-hai) **Base:** `main` ← **Head:** `feat/output-get-download-url` --- ### 📝 Commits (3) - [`997acd9`](https://github.com/Comfy-Org/comfy-python-sdk/commit/997acd968a911862e0b1b79633ef23abf63ce129) feat: output.get_download_url() + SDK User-Agent identification - [`efa87c4`](https://github.com/Comfy-Org/comfy-python-sdk/commit/efa87c410593fcbb47202d27ea1c194bbbbc2939) harden: reject CR/LF in client_info to prevent header injection - [`0faed84`](https://github.com/Comfy-Org/comfy-python-sdk/commit/0faed8440681a11b67c55adbc2c5c1c7074f5822) fix: treat overflowing X-Goog-Expires as no-expiry (return None) ### 📊 Changes **8 files changed** (+404 additions, -21 deletions) <details> <summary>View changed files</summary> 📝 `README.md` (+9 -0) 📝 `src/comfy_low/transport.py` (+117 -5) 📝 `src/comfy_sdk/__init__.py` (+2 -1) 📝 `src/comfy_sdk/client.py` (+4 -2) 📝 `src/comfy_sdk/outputs.py` (+36 -1) 📝 `tests/conftest.py` (+25 -12) ➕ `tests/test_get_download_url.py` (+170 -0) ➕ `tests/test_user_agent.py` (+41 -0) </details> ### 📄 Description ## 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). --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-26 22:40:44 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Comfy-Org/comfy-python-sdk#21
No description provided.