Skip to content

feat: add ids filter to GET /api/jobs for batch polling#14700

Open
mattmillerai wants to merge 2 commits into
masterfrom
matt/jobs-ids-filter
Open

feat: add ids filter to GET /api/jobs for batch polling#14700
mattmillerai wants to merge 2 commits into
masterfrom
matt/jobs-ids-filter

Conversation

@mattmillerai

Copy link
Copy Markdown
Contributor

ELI-5

GET /api/jobs lists jobs and already supports filtering by status and
workflow_id. If you've just queued a batch of prompts and want the status of
only that set, today you have to either fetch everything and filter client-side,
or call GET /api/jobs/{id} once per job. This PR adds an ids filter so you
can ask for exactly the jobs you care about in a single request:

GET /api/jobs?ids=<id1>,<id2>,<id3>

What changed

  • server.py (GET /api/jobs) — parse an optional comma-separated ids
    query parameter. The request is capped at 100 ids (count checked before
    validating the list, so an oversized request fails fast), and each id is
    validated with the existing validate_job_id helper. Overflow or a malformed
    id returns HTTP 400 with a client-safe message. The parsed list is passed down
    to get_all_jobs.
  • comfy_execution/jobs.py (get_all_jobs) — new optional
    ids: Optional[list[str]] = None argument that narrows the normalized job
    list to the requested ids when provided.

Semantics

  • Absent or empty ids means no filter — the full list is returned, exactly
    like the existing status / workflow_id params.
  • ids composes with the other filters; it only ever narrows the result.
  • Unknown ids (matching no job) are silently absent — not an error.

Judgment call for reviewers

validate_job_id accepts only canonical lowercase hyphenated UUIDs and rejects
any other spelling. This PR keeps the new filter consistent with the endpoint's
existing job-id validation (e.g. the cancel path) rather than normalizing
non-canonical forms (such as uppercase) before matching. Job ids are stored and
compared verbatim everywhere downstream, so accepting a non-canonical spelling
would silently miss every exact-match lookup. Flagging in case reviewers prefer
normalization instead.

Tests

tests-unit/jobs_list_test/ adds coverage for both layers:

  • get_all_jobs(ids=...) returns only the requested ids, composes with the
    status filter, treats an unknown id as silently absent, and treats absent /
    empty ids as no filter.
  • The endpoint's validation contract: a valid set narrows the response, an
    oversized set (>100) returns 400, and a malformed id returns 400.

As in tests-unit/jobs_cancel_test/, the HTTP layer is exercised against a
small aiohttp app whose handler mirrors the endpoint wiring, keeping the test
free of the heavy ComfyUI runtime.

Out of scope

The output_type and after (cursor) parameters are not implemented here —
that's separate, larger work to make /api/jobs honor its full contract.

Add an optional comma-separated `ids` query parameter to GET /api/jobs so a
caller can poll a known set of jobs in a single request instead of one call
per job. The filter narrows the result to the requested job ids and composes
with the existing status / workflow_id filters; an absent or empty `ids` means
no filter.

The handler caps the request at 100 ids (checked before validation) and
validates each id with the existing validate_job_id helper, returning HTTP 400
on overflow or a malformed id. get_all_jobs gains an optional ids argument that
narrows the normalized job list by id.

Adds unit coverage for the filter logic and the endpoint's validation contract.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b1b9edd6-dd21-4dca-ae0e-c80ae145177f

📥 Commits

Reviewing files that changed from the base of the PR and between 50e5270 and b656217.

📒 Files selected for processing (4)
  • comfy_execution/jobs.py
  • server.py
  • tests-unit/jobs_list_test/__init__.py
  • tests-unit/jobs_list_test/jobs_list_test.py

📝 Walkthrough

Walkthrough

This PR adds an ids query filter to the jobs listing feature. comfy_execution/jobs.py gains MAX_JOB_IDS_FILTER, JobIdsFilterError, and parse_ids_filter to parse, de-duplicate, cap, and validate comma-separated job ids, and get_all_jobs is updated to accept an ids parameter that restricts history lookups and final results. server.py's /api/jobs handler extracts, validates, and forwards the ids parameter, returning 400 on validation errors. A new test suite covers unit-level filtering/parsing logic and HTTP-level endpoint behavior.

Changes

Area Change
comfy_execution/jobs.py Added MAX_JOB_IDS_FILTER, JobIdsFilterError, parse_ids_filter; updated get_all_jobs to filter by ids
server.py Extracts, parses, validates ids query param on /api/jobs; returns 400 on error; passes filter to get_all_jobs
tests-unit/jobs_list_test/jobs_list_test.py New test suite for get_all_jobs filtering, parse_ids_filter parsing, and HTTP endpoint behavior

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GetJobsHandler
  participant parse_ids_filter
  participant get_all_jobs
  Client->>GetJobsHandler: GET /api/jobs?ids=...
  GetJobsHandler->>parse_ids_filter: ids_param
  alt invalid ids
    parse_ids_filter-->>GetJobsHandler: JobIdsFilterError(payload)
    GetJobsHandler-->>Client: 400 payload
  else valid ids
    parse_ids_filter-->>GetJobsHandler: ids_filter
    GetJobsHandler->>get_all_jobs: ids=ids_filter
    get_all_jobs-->>GetJobsHandler: jobs, total
    GetJobsHandler-->>Client: 200 jobs
  end
Loading

Estimated code review effort: Medium

Suggested labels: enhancement, api, backend

Suggested reviewers: (none specified)


A rabbit hopped through queries deep,
Filtered ids it learns to keep,
Capped at hundred, no more, no less,
Validated UUIDs, no mess,
Jobs now sorted, tidy heap. 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the new ids filter for GET /api/jobs and its batch-polling use case.
Description check ✅ Passed The description accurately describes the endpoint change, validation, semantics, and tests added by this PR.
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.

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

@github-actions github-actions 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.

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 5 finding(s).

Severity Count
🟢 Low 3
⚪ Nit 2

Panel: 8/8 reviewers contributed findings.

Comment thread server.py Outdated
Comment thread comfy_execution/jobs.py Outdated
Comment thread tests-unit/jobs_list_test/jobs_list_test.py
Comment thread comfy_execution/jobs.py Outdated
Comment thread server.py Outdated
- parse_ids_filter: one shared parser/validator for the ids query param, used
  by the /api/jobs handler AND its tests (no more hand-copied wiring that can
  drift from — and silently outlive a regression in — the shipped handler)
- present-but-empty ids (?ids=, ?ids=,,) is now a zero-match filter, not a
  silent 'return the entire job history'
- bounded history lookup when an ids filter is present: a batch poll costs
  O(requested ids), not O(total history)
- dedupe ids so the max-count cap bounds distinct values, not repeats
- .get('id') instead of j['id'] so a job missing its id degrades to no-match
  rather than a 500
@mattmillerai

Copy link
Copy Markdown
Contributor Author

Addressed all review-panel findings in b6562175:

  1. Empty ids returned everything (server.py) — ?ids= and ?ids=,, now parse to a present-but-empty filter (zero matches) instead of falling through to "no filter". None = no filter; [] = restrict to nothing.
  2. ids cap didn't bound the work (jobs.py) — with an ids filter present, get_all_jobs now looks the requested ids up directly in the id-keyed history instead of normalizing all of it, so a batch poll costs O(requested), not O(total history). Fixed the overstated MAX_JOB_IDS_FILTER comment.
  3. Tests reimplemented the handler (jobs_list_test) — extracted parsing/validation into a shared parse_ids_filter() that both the real /api/jobs handler and the tests call, so the HTTP tests exercise the shipped path and can't drift. Added direct unit tests for it.
  4. j['id'] KeyError risk (jobs.py) — now .get('id'), so a job missing its id degrades to no-match instead of a 500.
  5. Cap counted duplicates (server.py) — ids are de-duped (order-preserving), so the cap bounds distinct values; repeats no longer trip a 400. The over-limit test now uses distinct ids.

16 tests pass (added 6). Resolving these threads.

@mattmillerai mattmillerai added the Core Core team dependency label Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-coded Core Core team dependency cursor-review Trigger multi-model Cursor code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants