mirror of
https://github.com/Comfy-Org/comfy-typescript-sdk.git
synced 2026-11-04 23:47:27 +00:00
[PR #200] [MERGED] Label jobs with metadata at submit, and list jobs by label #200
Labels
No labels
audit:ai-readiness
audit:community
audit:github
audit:health-scoring
audit:policy
audit:release
audit:supply-chain
bug
cursor-review-pin
effort:medium
effort:small
effort:trivial
priority:nice-to-have
priority:recommended
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/comfy-typescript-sdk#200
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/Comfy-Org/comfy-typescript-sdk/pull/200
Author: @vqt123
Created: 10/5/2026
Status: ✅ Merged
Merged: 10/8/2026
Merged by: @vqt123
Base:
main← Head:vinh/dplat-2464-job-metadata📝 Commits (9)
9c2bd4efeat: label jobs with metadata at submit and list jobs by label699c51bfix: leave metadata errors as the base ComfyError, matching the Python SDKb2c198edocs: say what JobSummary.deploymentId names73ee238test: a cursor the list did not issue raises ComfyError invalid_cursor4075884test: Comfy Cloud's metadata_not_supported raises the base ComfyError842d8f3fix: retry a 429 on a listJobs page, keep Retry-After, read metadata leniently97dee11fix: keep a proto label, floor the 429 pause, filter listJobs locallyda061effix: match a listJobs filter value by the text the query sends8141382listJobs: reject a repeated next_cursor; compare filter values as the query sends them📊 Changes
12 files changed (+829 additions, -21 deletions)
View changed files
📝
CHANGELOG.md(+26 -0)📝
README.md(+48 -0)📝
src/low/index.ts(+3 -0)📝
src/low/transport.ts(+64 -2)📝
src/sdk/client.test.ts(+382 -1)📝
src/sdk/client.ts(+119 -11)📝
src/sdk/exceptions.test.ts(+7 -0)📝
src/sdk/exceptions.ts(+6 -1)📝
src/sdk/index.ts(+1 -1)📝
src/sdk/jobs.test.ts(+36 -0)📝
src/sdk/jobs.ts(+54 -0)📝
test/support/stub-server.ts(+83 -5)📄 Description
What changes for a developer
Before: a developer could not label a job, and the SDK had no way to list jobs. To find the jobs they ran for one of their customers, they had to keep their own record of job ids.
Now:
submit()takesmetadata, a map of their own string keys to string values. The returned job exposes it asjob.metadata.client.listJobs()lists their jobs newest first, filtered by those labels, and fetches the next page as they iterate.metadatasends the same request body as before.metadataas an empty object.ComfyErrorwith codemetadata_invalid, and the server's message names the key. A bad list filter raisesComfyErrorwith codeinvalid_metadata_filter. The Python SDK does the same.listJobs()yields a summary per job (id,status,createTime,updateTime,deploymentId,metadata). The list carries no outputs, so a caller reads the full job withclient.jobs.get(id).listJobs()retries a 429 on any page the same waysubmit()does (they share one helper). The walk carries on from that page instead of failing. The retry waits at least 1 s, so aRetry-After: 0no longer re-sends in a tight loop (this also fixessubmit()).listJobs()also checks each job against it and skips any job that does not hold every pair. A server that ignores the filter still yields only matching jobs, though one step of the loop may then fetch many pages. A value is compared as the text the query sent, so a JavaScript caller's number still matches.job.metadataand a summary'smetadatanever throw on a bad value. Ametadatathat is not an object (a self-hosted proxy sends its own as a string) reads as{}, and a value that is not a string is dropped. A label keyed__proto__is kept as an ordinary label.ComfyErrorfrom aComfymethod now carries the server'sRetry-AfterasretryAfter. Before, onlyQueueFullkept it, and every other error hadnull.Depends on the server change
This needs a Comfy API server change: the server stores
metadataon a job, returns it, and servesGET /api/v2/jobsfiltered by it. This SDK change must not be released until that server change is in production. Until then, a server ignoresmetadataand has noGET /api/v2/jobs. After it, labels work on jobs sent to a deployment's address. Comfy Cloud, the SDK's default host, does not support labels yet: it refuses a submit with labels (422metadata_not_supported) and answers the list with 501not_implemented, both raised as the baseComfyErrorwith that code. A self-hosted proxy keeps no labels: it rejects a labelled submit (422invalid_request), and its list ignores the label filter, so a filteredlistJobs()there yields nothing (the SDK's own check finds no match), and an unfiltered one yields every job withmetadataas{}. The README says both.The vendored
spec/openapi.yamldoes not have these fields yet, so nothing insrc/low/generated/changed. The hand-written transport adds themetadatabody field and alistJobsmethod on top of the generated types. When the server's spec change reaches this repo through the spec sync PR, the generated types catch up. That sync PR also addslistJobstoOPERATION_IDSinsrc/low/transport.ts, since the method already exists.Tests
In
src/sdk/client.test.ts, against the stub server:metadatasends it in the body, and the returned job exposes it.metadatasends a body with onlyworkflow, and the job'smetadatais{}.metadata_invalidraisesComfyError(notInvalidWorkflow) with that code, status 422, and the key in the message, without a retry. The stub sends this error only when the body carriesmetadata, and the test checks the body, so the test fails if the SDK stops sending labels.listJobs()sends each label asmetadata[key]=valuepluslimit, then followsnext_cursorto the second page and stops when it is absent. Both pages repeat the filters.listJobs()with no options sends no query parameters.listJobs()fillsmetadatawith{}and the time and deployment fields withnullwhen the server omits them.signalstops a list request in flight within 500 ms.metadata_not_supported(Comfy Cloud today) raisesComfyErrorcarrying that code, notInvalidWorkflow. Same stub rule and body check as above.Retry-After: 2on the second page is retried after 2 s, and the walk asks for page 1 once and page 2 twice.Retry-After: 2on the list raises aComfyErrorwhoseretryAfteris 2.metadatais a string, an array, or an object with non-string values yields{},{}, and only the string pairs.__proto__comes through as an own key, on a list item and onjob.metadata.Retry-After: 0waits 1 s before the re-send, onsubmit()and onlistJobs().listJobs()against a server that returns every job yields only the job that holds every pair.{ run: 7 }(a number, from an untyped caller) is sent asmetadata[run]=7and yields the job labelled run"7".invalid_metadata_filter, or a 400invalid_cursorfor a cursor the list did not issue, raisesComfyErrorcarrying that code.In
src/sdk/jobs.test.ts: a job read by id exposes the server'smetadata, or{}when it has none, when it is a string or an array, and keeps only the string values of an object. Insrc/sdk/exceptions.test.ts:toSdkErrorkeepsretryAfteron a non-QueueFullerror.Local runs:
pnpm lint,pnpm format:check,pnpm typecheck,pnpm check:spec-drift,pnpm buildand the public repo hygiene script all passed.pnpm testpassed: 668 tests passed, 5 skipped, across 28 files.Not run: a staging test of submit, read and list against a server with the change.
pnpm check:sdk-parityfailed locally on a Python SDK class (RouterRunResult) unrelated to this change.Risk: high, for the release order only: this must not merge before the server change is in production, and the merge waits for that. The code itself is low risk: Every new field and method is optional, and a submit without
metadatasends the same request as before. Nothing generated changed, and no error maps to a different class. Two existing behaviours change:retryAfteris now filled on errors other thanQueueFullwhen the server sentRetry-After, andsubmit()waits at least 1 s before re-sending after a 429.Summary by CodeRabbit
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.